]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/tools/makesyscalls.lua
MFV f83ac37f1e66: libbsdxml (expat) 2.4.3.
[FreeBSD/FreeBSD.git] / sys / tools / makesyscalls.lua
1 --
2 -- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 --
4 -- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
5 --
6 -- Redistribution and use in source and binary forms, with or without
7 -- modification, are permitted provided that the following conditions
8 -- are met:
9 -- 1. Redistributions of source code must retain the above copyright
10 --    notice, this list of conditions and the following disclaimer.
11 -- 2. Redistributions in binary form must reproduce the above copyright
12 --    notice, this list of conditions and the following disclaimer in the
13 --    documentation and/or other materials provided with the distribution.
14 --
15 -- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 -- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 -- SUCH DAMAGE.
26 --
27 -- $FreeBSD$
28 --
29
30
31 -- We generally assume that this script will be run by flua, however we've
32 -- carefully crafted modules for it that mimic interfaces provided by modules
33 -- available in ports.  Currently, this script is compatible with lua from ports
34 -- along with the compatible luafilesystem and lua-posix modules.
35 local lfs = require("lfs")
36 local unistd = require("posix.unistd")
37
38 local savesyscall = -1
39 local maxsyscall = -1
40 local generated_tag = "@" .. "generated"
41
42 -- Default configuration; any of these may get replaced by a configuration file
43 -- optionally specified.
44 local config = {
45         os_id_keyword = "FreeBSD",
46         abi_func_prefix = "",
47         sysnames = "syscalls.c",
48         sysproto = "../sys/sysproto.h",
49         sysproto_h = "_SYS_SYSPROTO_H_",
50         syshdr = "../sys/syscall.h",
51         sysmk = "../sys/syscall.mk",
52         syssw = "init_sysent.c",
53         syscallprefix = "SYS_",
54         switchname = "sysent",
55         namesname = "syscallnames",
56         systrace = "systrace_args.c",
57         capabilities_conf = "capabilities.conf",
58         capenabled = {},
59         compat_set = "native",
60         mincompat = 0,
61         abi_type_suffix = "",
62         abi_flags = "",
63         abi_flags_mask = 0,
64         abi_headers = "",
65         abi_intptr_t = "intptr_t",
66         abi_size_t = "size_t",
67         abi_u_long = "u_long",
68         abi_long = "long",
69         abi_semid_t = "semid_t",
70         abi_ptr_array_t = "",
71         ptr_intptr_t_cast = "intptr_t",
72         syscall_abi_change = "",
73         sys_abi_change = {},
74         syscall_no_abi_change = "",
75         sys_no_abi_change = {},
76         obsol = "",
77         obsol_dict = {},
78         unimpl = "",
79         unimpl_dict = {},
80 }
81
82 local config_modified = {}
83 local cleantmp = true
84 local tmpspace = "/tmp/sysent." .. unistd.getpid() .. "/"
85
86 local output_files = {
87         "sysnames",
88         "syshdr",
89         "sysmk",
90         "syssw",
91         "systrace",
92         "sysproto",
93 }
94
95 -- These ones we'll create temporary files for; generation purposes.
96 local temp_files = {
97         "sysaue",
98         "sysdcl",
99         "syscompat",
100         "syscompatdcl",
101         "sysent",
102         "sysinc",
103         "sysarg",
104         "sysprotoend",
105         "systracetmp",
106         "systraceret",
107 }
108
109 -- Opened files
110 local files = {}
111
112 local function cleanup()
113         for _, v in pairs(files) do
114                 assert(v:close())
115         end
116         if cleantmp then
117                 if lfs.dir(tmpspace) then
118                         for fname in lfs.dir(tmpspace) do
119                                 if fname ~= "." and fname ~= ".." then
120                                         assert(os.remove(tmpspace .. "/" ..
121                                             fname))
122                                 end
123                         end
124                 end
125
126                 if lfs.attributes(tmpspace) and not lfs.rmdir(tmpspace) then
127                         assert(io.stderr:write("Failed to clean up tmpdir: " ..
128                             tmpspace .. "\n"))
129                 end
130         else
131                 assert(io.stderr:write("Temp files left in " .. tmpspace ..
132                     "\n"))
133         end
134 end
135
136 local function abort(status, msg)
137         assert(io.stderr:write(msg .. "\n"))
138         cleanup()
139         os.exit(status)
140 end
141
142 -- Each entry should have a value so we can represent abi flags as a bitmask
143 -- for convenience.  One may also optionally provide an expr; this gets applied
144 -- to each argument type to indicate whether this argument is subject to ABI
145 -- change given the configured flags.
146 local known_abi_flags = {
147         long_size = {
148                 value   = 0x00000001,
149                 exprs   = {
150                         "_Contains[a-z_]*_long_",
151                         "^long [a-z0-9_]+$",
152                         "long [*]",
153                         "size_t [*]",
154                         -- semid_t is not included because it is only used
155                         -- as an argument or written out individually and
156                         -- said writes are handled by the ksem framework.
157                         -- Technically a sign-extension issue exists for
158                         -- arguments, but because semid_t is actually a file
159                         -- descriptor negative 32-bit values are invalid
160                         -- regardless of sign-extension.
161                 },
162         },
163         time_t_size = {
164                 value   = 0x00000002,
165                 exprs   = {
166                         "_Contains[a-z_]*_timet_",
167                 },
168         },
169         pointer_args = {
170                 value   = 0x00000004,
171         },
172         pointer_size = {
173                 value   = 0x00000008,
174                 exprs   = {
175                         "_Contains[a-z_]*_ptr_",
176                         "[*][*]",
177                 },
178         },
179         pair_64bit = {
180                 value   = 0x00000010,
181                 exprs   = {
182                         "^dev_t[ ]*$",
183                         "^id_t[ ]*$",
184                         "^off_t[ ]*$",
185                 },
186         },
187 }
188
189 local known_flags = {
190         STD             = 0x00000001,
191         OBSOL           = 0x00000002,
192         RESERVED        = 0x00000004,
193         UNIMPL          = 0x00000008,
194         NODEF           = 0x00000010,
195         NOARGS          = 0x00000020,
196         NOPROTO         = 0x00000040,
197         NOSTD           = 0x00000080,
198         NOTSTATIC       = 0x00000100,
199         CAPENABLED      = 0x00000200,
200         SYSMUX          = 0x00000400,
201
202         -- Compat flags start from here.  We have plenty of space.
203 }
204
205 -- All compat option entries should have five entries:
206 --      definition: The preprocessor macro that will be set for this
207 --      compatlevel: The level this compatibility should be included at.  This
208 --          generally represents the version of FreeBSD that it is compatible
209 --          with, but ultimately it's just the level of mincompat in which it's
210 --          included.
211 --      flag: The name of the flag in syscalls.master.
212 --      prefix: The prefix to use for _args and syscall prototype.  This will be
213 --          used as-is, without "_" or any other character appended.
214 --      descr: The description of this compat option in init_sysent.c comments.
215 -- The special "stdcompat" entry will cause the other five to be autogenerated.
216 local compat_option_sets = {
217         native = {
218                 {
219                         definition = "COMPAT_43",
220                         compatlevel = 3,
221                         flag = "COMPAT",
222                         prefix = "o",
223                         descr = "old",
224                 },
225                 { stdcompat = "FREEBSD4" },
226                 { stdcompat = "FREEBSD6" },
227                 { stdcompat = "FREEBSD7" },
228                 { stdcompat = "FREEBSD10" },
229                 { stdcompat = "FREEBSD11" },
230                 { stdcompat = "FREEBSD12" },
231                 { stdcompat = "FREEBSD13" },
232         },
233 }
234
235 -- compat_options will be resolved to a set from the configuration.
236 local compat_options
237
238 local function trim(s, char)
239         if s == nil then
240                 return nil
241         end
242         if char == nil then
243                 char = "%s"
244         end
245         return s:gsub("^" .. char .. "+", ""):gsub(char .. "+$", "")
246 end
247
248 -- config looks like a shell script; in fact, the previous makesyscalls.sh
249 -- script actually sourced it in.  It had a pretty common format, so we should
250 -- be fine to make various assumptions
251 local function process_config(file)
252         local cfg = {}
253         local comment_line_expr = "^%s*#.*"
254         -- We capture any whitespace padding here so we can easily advance to
255         -- the end of the line as needed to check for any trailing bogus bits.
256         -- Alternatively, we could drop the whitespace and instead try to
257         -- use a pattern to strip out the meaty part of the line, but then we
258         -- would need to sanitize the line for potentially special characters.
259         local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]*[`\"]?)"
260
261         if not file then
262                 return nil, "No file given"
263         end
264
265         local fh = assert(io.open(file))
266
267         for nextline in fh:lines() do
268                 -- Strip any whole-line comments
269                 nextline = nextline:gsub(comment_line_expr, "")
270                 -- Parse it into key, value pairs
271                 local key, value = nextline:match(line_expr)
272                 if key ~= nil and value ~= nil then
273                         local kvp = key .. "=" .. value
274                         key = trim(key)
275                         value = trim(value)
276                         local delim = value:sub(1,1)
277                         if delim == '"' then
278                                 local trailing_context
279
280                                 -- Strip off the key/value part
281                                 trailing_context = nextline:sub(kvp:len() + 1)
282                                 -- Strip off any trailing comment
283                                 trailing_context = trailing_context:gsub("#.*$",
284                                     "")
285                                 -- Strip off leading/trailing whitespace
286                                 trailing_context = trim(trailing_context)
287                                 if trailing_context ~= "" then
288                                         print(trailing_context)
289                                         abort(1, "Malformed line: " .. nextline)
290                                 end
291
292                                 value = trim(value, delim)
293                         else
294                                 -- Strip off potential comments
295                                 value = value:gsub("#.*$", "")
296                                 -- Strip off any padding whitespace
297                                 value = trim(value)
298                                 if value:match("%s") then
299                                         abort(1, "Malformed config line: " ..
300                                             nextline)
301                                 end
302                         end
303                         cfg[key] = value
304                 elseif not nextline:match("^%s*$") then
305                         -- Make sure format violations don't get overlooked
306                         -- here, but ignore blank lines.  Comments are already
307                         -- stripped above.
308                         abort(1, "Malformed config line: " .. nextline)
309                 end
310         end
311
312         assert(io.close(fh))
313         return cfg
314 end
315
316 local function grab_capenabled(file, open_fail_ok)
317         local capentries = {}
318         local commentExpr = "#.*"
319
320         if file == nil then
321                 print "No file"
322                 return {}
323         end
324
325         local fh = io.open(file)
326         if fh == nil then
327                 if not open_fail_ok then
328                         abort(1, "Failed to open " .. file)
329                 end
330                 return {}
331         end
332
333         for nextline in fh:lines() do
334                 -- Strip any comments
335                 nextline = nextline:gsub(commentExpr, "")
336                 if nextline ~= "" then
337                         capentries[nextline] = true
338                 end
339         end
340
341         assert(io.close(fh))
342         return capentries
343 end
344
345 local function process_compat()
346         local nval = 0
347         for _, v in pairs(known_flags) do
348                 if v > nval then
349                         nval = v
350                 end
351         end
352
353         nval = nval << 1
354         for _, v in pairs(compat_options) do
355                 if v["stdcompat"] ~= nil then
356                         local stdcompat = v["stdcompat"]
357                         v["definition"] = "COMPAT_" .. stdcompat:upper()
358                         v["compatlevel"] = tonumber(stdcompat:match("([0-9]+)$"))
359                         v["flag"] = stdcompat:gsub("FREEBSD", "COMPAT")
360                         v["prefix"] = stdcompat:lower() .. "_"
361                         v["descr"] = stdcompat:lower()
362                 end
363
364                 local tmpname = "sys" .. v["flag"]:lower()
365                 local dcltmpname = tmpname .. "dcl"
366                 files[tmpname] = io.tmpfile()
367                 files[dcltmpname] = io.tmpfile()
368                 v["tmp"] = tmpname
369                 v["dcltmp"] = dcltmpname
370
371                 known_flags[v["flag"]] = nval
372                 v["mask"] = nval
373                 nval = nval << 1
374
375                 v["count"] = 0
376         end
377 end
378
379 local function process_abi_flags()
380         local flags, mask = config["abi_flags"], 0
381         for txtflag in flags:gmatch("([^|]+)") do
382                 if known_abi_flags[txtflag] == nil then
383                         abort(1, "Unknown abi_flag: " .. txtflag)
384                 end
385
386                 mask = mask | known_abi_flags[txtflag]["value"]
387         end
388
389         config["abi_flags_mask"] = mask
390 end
391
392 local function process_obsol()
393         local obsol = config["obsol"]
394         for syscall in obsol:gmatch("([^ ]+)") do
395                 config["obsol_dict"][syscall] = true
396         end
397 end
398
399 local function process_unimpl()
400         local unimpl = config["unimpl"]
401         for syscall in unimpl:gmatch("([^ ]+)") do
402                 config["unimpl_dict"][syscall] = true
403         end
404 end
405
406 local function process_syscall_abi_change()
407         local changes_abi = config["syscall_abi_change"]
408         for syscall in changes_abi:gmatch("([^ ]+)") do
409                 config["sys_abi_change"][syscall] = true
410         end
411
412         local no_changes = config["syscall_no_abi_change"]
413         for syscall in no_changes:gmatch("([^ ]+)") do
414                 config["sys_no_abi_change"][syscall] = true
415         end
416 end
417
418 local function abi_changes(name)
419         if known_abi_flags[name] == nil then
420                 abort(1, "abi_changes: unknown flag: " .. name)
421         end
422
423         return config["abi_flags_mask"] & known_abi_flags[name]["value"] ~= 0
424 end
425
426 local function strip_abi_prefix(funcname)
427         local abiprefix = config["abi_func_prefix"]
428         local stripped_name
429         if funcname == nil then
430                 return nil
431         end
432         if abiprefix ~= "" and funcname:find("^" .. abiprefix) then
433                 stripped_name = funcname:gsub("^" .. abiprefix, "")
434         else
435                 stripped_name = funcname
436         end
437
438         return stripped_name
439 end
440
441 local function read_file(tmpfile)
442         if files[tmpfile] == nil then
443                 print("Not found: " .. tmpfile)
444                 return
445         end
446
447         local fh = files[tmpfile]
448         assert(fh:seek("set"))
449         return assert(fh:read("a"))
450 end
451
452 local function write_line(tmpfile, line)
453         if files[tmpfile] == nil then
454                 print("Not found: " .. tmpfile)
455                 return
456         end
457         assert(files[tmpfile]:write(line))
458 end
459
460 local function write_line_pfile(tmppat, line)
461         for k in pairs(files) do
462                 if k:match(tmppat) ~= nil then
463                         assert(files[k]:write(line))
464                 end
465         end
466 end
467
468 -- Check both literal intptr_t and the abi version because this needs
469 -- to work both before and after the substitution
470 local function isptrtype(type)
471         return type:find("*") or type:find("caddr_t") or
472             type:find("intptr_t") or type:find(config['abi_intptr_t'])
473 end
474
475 local function isptrarraytype(type)
476         return type:find("[*][*]") or type:find("[*][ ]*const[ ]*[*]")
477 end
478
479 -- Find types that are always 64-bits wide
480 local function is64bittype(type)
481         return type:find("^dev_t[ ]*$") or type:find("^id_t[ ]*$") or type:find("^off_t[ ]*$")
482 end
483
484 local process_syscall_def
485
486 -- These patterns are processed in order on any line that isn't empty.
487 local pattern_table = {
488         {
489                 pattern = "%s*$" .. config['os_id_keyword'],
490                 process = function(_, _)
491                         -- Ignore... ID tag
492                 end,
493         },
494         {
495                 dump_prevline = true,
496                 pattern = "^#%s*include",
497                 process = function(line)
498                         line = line .. "\n"
499                         write_line('sysinc', line)
500                 end,
501         },
502         {
503                 dump_prevline = true,
504                 pattern = "^#",
505                 process = function(line)
506                         if line:find("^#%s*if") then
507                                 savesyscall = maxsyscall
508                         elseif line:find("^#%s*else") then
509                                 maxsyscall = savesyscall
510                         end
511                         line = line .. "\n"
512                         write_line('sysent', line)
513                         write_line('sysdcl', line)
514                         write_line('sysarg', line)
515                         write_line_pfile('syscompat[0-9]*$', line)
516                         write_line('sysnames', line)
517                         write_line_pfile('systrace.*', line)
518                 end,
519         },
520         {
521                 dump_prevline = true,
522                 pattern = "%%ABI_HEADERS%%",
523                 process = function()
524                         if config['abi_headers'] ~= "" then
525                                 line = config['abi_headers'] .. "\n"
526                                 write_line('sysinc', line)
527                         end
528                 end,
529         },
530         {
531                 -- Buffer anything else
532                 pattern = ".+",
533                 process = function(line, prevline)
534                         local incomplete = line:find("\\$") ~= nil
535                         -- Lines that end in \ get the \ stripped
536                         -- Lines that start with a syscall number, prepend \n
537                         line = trim(line):gsub("\\$", "")
538                         if line:find("^[0-9]") and prevline then
539                                 process_syscall_def(prevline)
540                                 prevline = nil
541                         end
542
543                         prevline = (prevline or '') .. line
544                         incomplete = incomplete or prevline:find(",$") ~= nil
545                         incomplete = incomplete or prevline:find("{") ~= nil and
546                             prevline:find("}") == nil
547                         if prevline:find("^[0-9]") and not incomplete then
548                                 process_syscall_def(prevline)
549                                 prevline = nil
550                         end
551
552                         return prevline
553                 end,
554         },
555 }
556
557 local function process_sysfile(file)
558         local capentries = {}
559         local commentExpr = "^%s*;.*"
560
561         if file == nil then
562                 print "No file"
563                 return {}
564         end
565
566         local fh = io.open(file)
567         if fh == nil then
568                 print("Failed to open " .. file)
569                 return {}
570         end
571
572         local function do_match(nextline, prevline)
573                 local pattern, handler, dump
574                 for _, v in pairs(pattern_table) do
575                         pattern = v['pattern']
576                         handler = v['process']
577                         dump = v['dump_prevline']
578                         if nextline:match(pattern) then
579                                 if dump and prevline then
580                                         process_syscall_def(prevline)
581                                         prevline = nil
582                                 end
583
584                                 return handler(nextline, prevline)
585                         end
586                 end
587
588                 abort(1, "Failed to handle: " .. nextline)
589         end
590
591         local prevline
592         for nextline in fh:lines() do
593                 -- Strip any comments
594                 nextline = nextline:gsub(commentExpr, "")
595                 if nextline ~= "" then
596                         prevline = do_match(nextline, prevline)
597                 end
598         end
599
600         -- Dump any remainder
601         if prevline ~= nil and prevline:find("^[0-9]") then
602                 process_syscall_def(prevline)
603         end
604
605         assert(io.close(fh))
606         return capentries
607 end
608
609 local function get_mask(flags)
610         local mask = 0
611         for _, v in ipairs(flags) do
612                 if known_flags[v] == nil then
613                         abort(1, "Checking for unknown flag " .. v)
614                 end
615
616                 mask = mask | known_flags[v]
617         end
618
619         return mask
620 end
621
622 local function get_mask_pat(pflags)
623         local mask = 0
624         for k, v in pairs(known_flags) do
625                 if k:find(pflags) then
626                         mask = mask | v
627                 end
628         end
629
630         return mask
631 end
632
633 local function align_sysent_comment(col)
634         write_line("sysent", "\t")
635         col = col + 8 - col % 8
636         while col < 56 do
637                 write_line("sysent", "\t")
638                 col = col + 8
639         end
640 end
641
642 local function strip_arg_annotations(arg)
643         arg = arg:gsub("_Contains_[^ ]*[_)] ?", "")
644         arg = arg:gsub("_In[^ ]*[_)] ?", "")
645         arg = arg:gsub("_Out[^ ]*[_)] ?", "")
646         return trim(arg)
647 end
648
649 local function check_abi_changes(arg)
650         for k, v in pairs(known_abi_flags) do
651                 local exprs = v["exprs"]
652                 if abi_changes(k) and exprs ~= nil then
653                         for _, e in pairs(exprs) do
654                                 if arg:find(e) then
655                                         return true
656                                 end
657                         end
658                 end
659         end
660
661         return false
662 end
663
664 local function process_args(args)
665         local funcargs = {}
666         local changes_abi = false
667
668         for arg in args:gmatch("([^,]+)") do
669                 local arg_abi_change = check_abi_changes(arg)
670                 changes_abi = changes_abi or arg_abi_change
671
672                 arg = strip_arg_annotations(arg)
673
674                 local argname = arg:match("([^* ]+)$")
675
676                 -- argtype is... everything else.
677                 local argtype = trim(arg:gsub(argname .. "$", ""), nil)
678
679                 if argtype == "" and argname == "void" then
680                         goto out
681                 end
682
683                 -- is64bittype() needs a bare type so check it after argname
684                 -- is removed
685                 changes_abi = changes_abi or (abi_changes("pair_64bit") and is64bittype(argtype))
686
687                 argtype = argtype:gsub("intptr_t", config["abi_intptr_t"])
688                 argtype = argtype:gsub("semid_t", config["abi_semid_t"])
689                 if isptrtype(argtype) then
690                         argtype = argtype:gsub("size_t", config["abi_size_t"])
691                         argtype = argtype:gsub("^long", config["abi_long"]);
692                         argtype = argtype:gsub("^u_long", config["abi_u_long"]);
693                         argtype = argtype:gsub("^const u_long", "const " .. config["abi_u_long"]);
694                 elseif argtype:find("^long$") then
695                         argtype = config["abi_long"]
696                 end
697                 if isptrarraytype(argtype) and config["abi_ptr_array_t"] ~= "" then
698                         -- `* const *` -> `**`
699                         argtype = argtype:gsub("[*][ ]*const[ ]*[*]", "**")
700                         -- e.g., `struct aiocb **` -> `uint32_t *`
701                         argtype = argtype:gsub("[^*]*[*]", config["abi_ptr_array_t"] .. " ", 1)
702                 end
703
704                 -- XX TODO: Forward declarations? See: sysstubfwd in CheriBSD
705                 if arg_abi_change then
706                         local abi_type_suffix = config["abi_type_suffix"]
707                         argtype = argtype:gsub("(struct [^ ]*)", "%1" ..
708                             abi_type_suffix)
709                         argtype = argtype:gsub("(union [^ ]*)", "%1" ..
710                             abi_type_suffix)
711                 end
712
713                 if abi_changes("pair_64bit") and is64bittype(argtype) then
714                         if #funcargs % 2 == 1 then
715                                 funcargs[#funcargs + 1] = {
716                                         type = "int",
717                                         name = "_pad",
718                                 }
719                         end
720                         funcargs[#funcargs + 1] = {
721                                 type = "uint32_t",
722                                 name = argname .. "1",
723                         }
724                         funcargs[#funcargs + 1] = {
725                                 type = "uint32_t",
726                                 name = argname .. "2",
727                         }
728                 else
729                         funcargs[#funcargs + 1] = {
730                                 type = argtype,
731                                 name = argname,
732                         }
733                 end
734         end
735
736         ::out::
737         return funcargs, changes_abi
738 end
739
740 local function handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
741     auditev, syscallret, funcname, funcalias, funcargs, argalias)
742         local argssize
743
744         if flags & known_flags["SYSMUX"] ~= 0 then
745                 argssize = "0"
746         elseif #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
747                 argssize = "AS(" .. argalias .. ")"
748         else
749                 argssize = "0"
750         end
751
752         write_line("systrace", string.format([[
753         /* %s */
754         case %d: {
755 ]], funcname, sysnum))
756         write_line("systracetmp", string.format([[
757         /* %s */
758         case %d:
759 ]], funcname, sysnum))
760         write_line("systraceret", string.format([[
761         /* %s */
762         case %d:
763 ]], funcname, sysnum))
764
765         if #funcargs > 0 and flags & known_flags["SYSMUX"] == 0 then
766                 write_line("systracetmp", "\t\tswitch (ndx) {\n")
767                 write_line("systrace", string.format(
768                     "\t\tstruct %s *p = params;\n", argalias))
769
770
771                 local argtype, argname, desc, padding
772                 padding = ""
773                 for idx, arg in ipairs(funcargs) do
774                         argtype = arg["type"]
775                         argname = arg["name"]
776
777                         argtype = trim(argtype:gsub("__restrict$", ""), nil)
778                         if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
779                                 write_line("systracetmp", "#ifdef PAD64_REQUIRED\n")
780                         end
781                         -- Pointer arg?
782                         if argtype:find("*") then
783                                 desc = "userland " .. argtype
784                         else
785                                 desc = argtype;
786                         end
787                         write_line("systracetmp", string.format(
788                             "\t\tcase %d%s:\n\t\t\tp = \"%s\";\n\t\t\tbreak;\n",
789                             idx - 1, padding, desc))
790                         if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
791                                 padding = " - _P_"
792                                 write_line("systracetmp", "#define _P_ 0\n#else\n#define _P_ 1\n#endif\n")
793                         end
794
795                         if isptrtype(argtype) then
796                                 write_line("systrace", string.format(
797                                     "\t\tuarg[a++] = (%s)p->%s; /* %s */\n",
798                                     config["ptr_intptr_t_cast"],
799                                     argname, argtype))
800                         elseif argtype == "union l_semun" then
801                                 write_line("systrace", string.format(
802                                     "\t\tuarg[a++] = p->%s.buf; /* %s */\n",
803                                     argname, argtype))
804                         elseif argtype:sub(1,1) == "u" or argtype == "size_t" then
805                                 write_line("systrace", string.format(
806                                     "\t\tuarg[a++] = p->%s; /* %s */\n",
807                                     argname, argtype))
808                         else
809                                 if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
810                                         write_line("systrace", "#ifdef PAD64_REQUIRED\n")
811                                 end
812                                 write_line("systrace", string.format(
813                                     "\t\tiarg[a++] = p->%s; /* %s */\n",
814                                     argname, argtype))
815                                 if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
816                                         write_line("systrace", "#endif\n")
817                                 end
818                         end
819                 end
820
821                 write_line("systracetmp",
822                     "\t\tdefault:\n\t\t\tbreak;\n\t\t};\n")
823                 if padding ~= "" then
824                         write_line("systracetmp", "#undef _P_\n\n")
825                 end
826
827                 write_line("systraceret", string.format([[
828                 if (ndx == 0 || ndx == 1)
829                         p = "%s";
830                 break;
831 ]], syscallret))
832         end
833         local n_args = #funcargs
834         if flags & known_flags["SYSMUX"] ~= 0 then
835                 n_args = 0
836         end
837         write_line("systrace", string.format(
838             "\t\t*n_args = %d;\n\t\tbreak;\n\t}\n", n_args))
839         write_line("systracetmp", "\t\tbreak;\n")
840
841         local nargflags = get_mask({"NOARGS", "NOPROTO", "NODEF"})
842         if flags & nargflags == 0 then
843                 if #funcargs > 0 then
844                         write_line("sysarg", string.format("struct %s {\n",
845                             argalias))
846                         for _, v in ipairs(funcargs) do
847                                 local argname, argtype = v["name"], v["type"]
848                                 if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
849                                         write_line("sysarg", "#ifdef PAD64_REQUIRED\n")
850                                 end
851                                 write_line("sysarg", string.format(
852                                     "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
853                                     argname, argtype,
854                                     argtype, argname,
855                                     argname, argtype))
856                                 if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
857                                         write_line("sysarg", "#endif\n")
858                                 end
859                         end
860                         write_line("sysarg", "};\n")
861                 else
862                         write_line("sysarg", string.format(
863                             "struct %s {\n\tregister_t dummy;\n};\n", argalias))
864                 end
865         end
866
867         local protoflags = get_mask({"NOPROTO", "NODEF"})
868         if flags & protoflags == 0 then
869                 if funcname == "nosys" or funcname == "lkmnosys" or
870                     funcname == "sysarch" or funcname:find("^freebsd") or
871                     funcname:find("^linux") then
872                         write_line("sysdcl", string.format(
873                             "%s\t%s(struct thread *, struct %s *)",
874                             rettype, funcname, argalias))
875                 else
876                         write_line("sysdcl", string.format(
877                             "%s\tsys_%s(struct thread *, struct %s *)",
878                             rettype, funcname, argalias))
879                 end
880                 write_line("sysdcl", ";\n")
881                 write_line("sysaue", string.format("#define\t%sAUE_%s\t%s\n",
882                     config['syscallprefix'], funcalias, auditev))
883         end
884
885         write_line("sysent",
886             string.format("\t{ .sy_narg = %s, .sy_call = (sy_call_t *)", argssize))
887         local column = 8 + 2 + #argssize + 15
888
889         if flags & known_flags["SYSMUX"] ~= 0 then
890                 write_line("sysent", string.format(
891                     "nosys, .sy_auevent = AUE_NULL, " ..
892                     ".sy_flags = %s, .sy_thrcnt = SY_THR_STATIC },",
893                     sysflags))
894                 column = column + #"nosys" + #"AUE_NULL" + 3
895         elseif flags & known_flags["NOSTD"] ~= 0 then
896                 write_line("sysent", string.format(
897                     "lkmressys, .sy_auevent = AUE_NULL, " ..
898                     ".sy_flags = %s, .sy_thrcnt = SY_THR_ABSENT },",
899                     sysflags))
900                 column = column + #"lkmressys" + #"AUE_NULL" + 3
901         else
902                 if funcname == "nosys" or funcname == "lkmnosys" or
903                     funcname == "sysarch" or funcname:find("^freebsd") or
904                     funcname:find("^linux") then
905                         write_line("sysent", string.format(
906                             "%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
907                             funcname, auditev, sysflags, thr_flag))
908                         column = column + #funcname + #auditev + #sysflags + 3
909                 else
910                         write_line("sysent", string.format(
911                             "sys_%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
912                             funcname, auditev, sysflags, thr_flag))
913                         column = column + #funcname + #auditev + #sysflags + 7
914                 end
915         end
916
917         align_sysent_comment(column)
918         write_line("sysent", string.format("/* %d = %s */\n",
919             sysnum, funcalias))
920         write_line("sysnames", string.format("\t\"%s\",\t\t\t/* %d = %s */\n",
921             funcalias, sysnum, funcalias))
922
923         if flags & known_flags["NODEF"] == 0 then
924                 write_line("syshdr", string.format("#define\t%s%s\t%d\n",
925                     config['syscallprefix'], funcalias, sysnum))
926                 write_line("sysmk", string.format(" \\\n\t%s.o",
927                     funcalias))
928         end
929 end
930
931 local function handle_obsol(sysnum, funcname, comment)
932         write_line("sysent",
933             "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
934             ".sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT },")
935         align_sysent_comment(34)
936
937         write_line("sysent", string.format("/* %d = obsolete %s */\n",
938             sysnum, comment))
939         write_line("sysnames", string.format(
940             "\t\"obs_%s\",\t\t\t/* %d = obsolete %s */\n",
941             funcname, sysnum, comment))
942         write_line("syshdr", string.format("\t\t\t\t/* %d is obsolete %s */\n",
943             sysnum, comment))
944 end
945
946 local function handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
947     auditev, funcname, funcalias, funcargs, argalias)
948         local argssize, out, outdcl, wrap, prefix, descr
949
950         if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
951                 argssize = "AS(" .. argalias .. ")"
952         else
953                 argssize = "0"
954         end
955
956         for _, v in pairs(compat_options) do
957                 if flags & v["mask"] ~= 0 then
958                         if config["mincompat"] > v["compatlevel"] then
959                                 funcname = strip_abi_prefix(funcname)
960                                 funcname = v["prefix"] .. funcname
961                                 return handle_obsol(sysnum, funcname, funcname)
962                         end
963                         v["count"] = v["count"] + 1
964                         out = v["tmp"]
965                         outdcl = v["dcltmp"]
966                         wrap = v["flag"]:lower()
967                         prefix = v["prefix"]
968                         descr = v["descr"]
969                         goto compatdone
970                 end
971         end
972
973         ::compatdone::
974         local dprotoflags = get_mask({"NOPROTO", "NODEF"})
975         local nargflags = dprotoflags | known_flags["NOARGS"]
976         if #funcargs > 0 and flags & nargflags == 0 then
977                 write_line(out, string.format("struct %s {\n", argalias))
978                 for _, v in ipairs(funcargs) do
979                         local argname, argtype = v["name"], v["type"]
980                         write_line(out, string.format(
981                             "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
982                             argname, argtype,
983                             argtype, argname,
984                             argname, argtype))
985                 end
986                 write_line(out, "};\n")
987         elseif flags & nargflags == 0 then
988                 write_line("sysarg", string.format(
989                     "struct %s {\n\tregister_t dummy;\n};\n", argalias))
990         end
991         if flags & dprotoflags == 0 then
992                 write_line(outdcl, string.format(
993                     "%s\t%s%s(struct thread *, struct %s *);\n",
994                     rettype, prefix, funcname, argalias))
995                 write_line("sysaue", string.format(
996                     "#define\t%sAUE_%s%s\t%s\n", config['syscallprefix'],
997                     prefix, funcname, auditev))
998         end
999
1000         if flags & known_flags['NOSTD'] ~= 0 then
1001                 write_line("sysent", string.format(
1002                     "\t{ .sy_narg = %s, .sy_call = (sy_call_t *)%s, " ..
1003                     ".sy_auevent = %s, .sy_flags = 0, " ..
1004                     ".sy_thrcnt = SY_THR_ABSENT },",
1005                     "0", "lkmressys", "AUE_NULL"))
1006                 align_sysent_comment(8 + 2 + #"0" + 15 + #"lkmressys" +
1007                     #"AUE_NULL" + 3)
1008         else
1009                 write_line("sysent", string.format(
1010                     "\t{ %s(%s,%s), .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
1011                     wrap, argssize, funcname, auditev, sysflags, thr_flag))
1012                 align_sysent_comment(8 + 9 + #argssize + 1 + #funcname +
1013                     #auditev + #sysflags + 4)
1014         end
1015
1016         write_line("sysent", string.format("/* %d = %s %s */\n",
1017             sysnum, descr, funcalias))
1018         write_line("sysnames", string.format(
1019             "\t\"%s.%s\",\t\t/* %d = %s %s */\n",
1020             wrap, funcalias, sysnum, descr, funcalias))
1021         -- Do not provide freebsdN_* symbols in libc for < FreeBSD 7
1022         local nosymflags = get_mask({"COMPAT", "COMPAT4", "COMPAT6"})
1023         if flags & nosymflags ~= 0 then
1024                 write_line("syshdr", string.format(
1025                     "\t\t\t\t/* %d is %s %s */\n",
1026                     sysnum, descr, funcalias))
1027         elseif flags & known_flags["NODEF"] == 0 then
1028                 write_line("syshdr", string.format("#define\t%s%s%s\t%d\n",
1029                     config['syscallprefix'], prefix, funcalias, sysnum))
1030                 write_line("sysmk", string.format(" \\\n\t%s%s.o",
1031                     prefix, funcalias))
1032         end
1033 end
1034
1035 local function handle_unimpl(sysnum, sysstart, sysend, comment)
1036         if sysstart == nil and sysend == nil then
1037                 sysstart = tonumber(sysnum)
1038                 sysend = tonumber(sysnum)
1039         end
1040
1041         sysnum = sysstart
1042         while sysnum <= sysend do
1043                 write_line("sysent", string.format(
1044                     "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
1045                     ".sy_auevent = AUE_NULL, .sy_flags = 0, " ..
1046                     ".sy_thrcnt = SY_THR_ABSENT },\t\t\t/* %d = %s */\n",
1047                     sysnum, comment))
1048                 write_line("sysnames", string.format(
1049                     "\t\"#%d\",\t\t\t/* %d = %s */\n",
1050                     sysnum, sysnum, comment))
1051                 sysnum = sysnum + 1
1052         end
1053 end
1054
1055 local function handle_reserved(sysnum, sysstart, sysend, comment)
1056         handle_unimpl(sysnum, sysstart, sysend, "reserved for local use")
1057 end
1058
1059 process_syscall_def = function(line)
1060         local sysstart, sysend, flags, funcname, sysflags
1061         local thr_flag, syscallret
1062         local orig = line
1063         flags = 0
1064         thr_flag = "SY_THR_STATIC"
1065
1066         -- Parse out the interesting information first
1067         local initialExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s*"
1068         local sysnum, auditev, allflags = line:match(initialExpr)
1069
1070         if sysnum == nil or auditev == nil or allflags == nil then
1071                 -- XXX TODO: Better?
1072                 abort(1, "Completely malformed: " .. line)
1073         end
1074
1075         if sysnum:find("-") then
1076                 sysstart, sysend = sysnum:match("^([%d]+)-([%d]+)$")
1077                 if sysstart == nil or sysend == nil then
1078                         abort(1, "Malformed range: " .. sysnum)
1079                 end
1080                 sysnum = nil
1081                 sysstart = tonumber(sysstart)
1082                 sysend = tonumber(sysend)
1083                 if sysstart ~= maxsyscall + 1 then
1084                         abort(1, "syscall number out of sync, missing " ..
1085                             maxsyscall + 1)
1086                 end
1087         else
1088                 sysnum = tonumber(sysnum)
1089                 if sysnum ~= maxsyscall + 1 then
1090                         abort(1, "syscall number out of sync, missing " ..
1091                             maxsyscall + 1)
1092                 end
1093         end
1094
1095         -- Split flags
1096         for flag in allflags:gmatch("([^|]+)") do
1097                 if known_flags[flag] == nil then
1098                         abort(1, "Unknown flag " .. flag .. " for " ..  sysnum)
1099                 end
1100                 flags = flags | known_flags[flag]
1101         end
1102
1103         if (flags & get_mask({"RESERVED", "UNIMPL"})) == 0 and sysnum == nil then
1104                 abort(1, "Range only allowed with RESERVED and UNIMPL: " .. line)
1105         end
1106
1107         if (flags & known_flags["NOTSTATIC"]) ~= 0 then
1108                 thr_flag = "SY_THR_ABSENT"
1109         end
1110
1111         -- Strip earlier bits out, leave declaration + alt
1112         line = line:gsub("^.+" .. allflags .. "%s*", "")
1113
1114         local decl_fnd = line:find("^{") ~= nil
1115         if decl_fnd and line:find("}") == nil then
1116                 abort(1, "Malformed, no closing brace: " .. line)
1117         end
1118
1119         local decl, alt
1120         if decl_fnd then
1121                 line = line:gsub("^{", "")
1122                 decl, alt = line:match("([^}]*)}[%s]*(.*)$")
1123         else
1124                 alt = line
1125         end
1126
1127         if decl == nil and alt == nil then
1128                 abort(1, "Malformed bits: " .. line)
1129         end
1130
1131         local funcalias, funcomment, argalias, rettype, args
1132         if not decl_fnd and alt ~= nil and alt ~= "" then
1133                 -- Peel off one entry for name
1134                 funcname = trim(alt:match("^([^%s]+)"), nil)
1135                 alt = alt:gsub("^([^%s]+)[%s]*", "")
1136         end
1137         -- Do we even need it?
1138         if flags & get_mask({"OBSOL", "UNIMPL"}) ~= 0 then
1139                 local NF = 0
1140                 for _ in orig:gmatch("[^%s]+") do
1141                         NF = NF + 1
1142                 end
1143
1144                 funcomment = funcname or ''
1145                 if NF < 6 then
1146                         funcomment = funcomment .. " " .. alt
1147                 end
1148
1149                 funcomment = trim(funcomment)
1150
1151 --              if funcname ~= nil then
1152 --              else
1153 --                      funcomment = trim(alt)
1154 --              end
1155                 goto skipalt
1156         end
1157
1158         if alt ~= nil and alt ~= "" then
1159                 local altExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)"
1160                 funcalias, argalias, rettype = alt:match(altExpr)
1161                 funcalias = trim(funcalias)
1162                 if funcalias == nil or argalias == nil or rettype == nil then
1163                         abort(1, "Malformed alt: " .. line)
1164                 end
1165         end
1166         if decl_fnd then
1167                 -- Don't clobber rettype set in the alt information
1168                 if rettype == nil then
1169                         rettype = "int"
1170                 end
1171                 -- Peel off the return type
1172                 syscallret = line:match("([^%s]+)%s")
1173                 line = line:match("[^%s]+%s(.+)")
1174                 -- Pointer incoming
1175                 if line:sub(1,1) == "*" then
1176                         syscallret = syscallret .. " "
1177                 end
1178                 while line:sub(1,1) == "*" do
1179                         line = line:sub(2)
1180                         syscallret = syscallret .. "*"
1181                 end
1182                 funcname = line:match("^([^(]+)%(")
1183                 if funcname == nil then
1184                         abort(1, "Not a signature? " .. line)
1185                 end
1186                 args = line:match("^[^(]+%((.+)%)[^)]*$")
1187                 args = trim(args, '[,%s]')
1188         end
1189
1190         ::skipalt::
1191
1192         if funcname == nil then
1193                 funcname = funcalias
1194         end
1195
1196         funcname = trim(funcname)
1197
1198         if config["obsol_dict"][funcname] then
1199                 local compat_prefix = ""
1200                 for _, v in pairs(compat_options) do
1201                         if flags & v["mask"] ~= 0 then
1202                                 compat_prefix = v["prefix"]
1203                                 goto obsol_compat_done
1204                         end
1205                 end
1206                 ::obsol_compat_done::
1207                 args = nil
1208                 flags = known_flags['OBSOL']
1209                 funcomment = compat_prefix .. funcname
1210         end
1211         if config["unimpl_dict"][funcname] then
1212                 flags = known_flags['UNIMPL']
1213                 funcomment = funcname
1214         end
1215
1216         sysflags = "0"
1217
1218         -- NODEF events do not get audited
1219         if flags & known_flags['NODEF'] ~= 0 then
1220                 auditev = 'AUE_NULL'
1221         end
1222
1223         -- If applicable; strip the ABI prefix from the name
1224         local stripped_name = strip_abi_prefix(funcname)
1225
1226         if flags & known_flags['CAPENABLED'] ~= 0 or
1227             config["capenabled"][funcname] ~= nil or
1228             config["capenabled"][stripped_name] ~= nil then
1229                 sysflags = "SYF_CAPENABLED"
1230         end
1231
1232         local funcargs = {}
1233         local changes_abi = false
1234         if args ~= nil then
1235                 funcargs, changes_abi = process_args(args)
1236         end
1237         if config["sys_no_abi_change"][funcname] then
1238                 changes_abi = false
1239         end
1240         local noproto = config["abi_flags"] ~= "" and not changes_abi
1241
1242         local argprefix = ''
1243         local funcprefix = ''
1244         if abi_changes("pointer_args") then
1245                 for _, v in ipairs(funcargs) do
1246                         if isptrtype(v["type"]) then
1247                                 if config["sys_no_abi_change"][funcname] then
1248                                         print("WARNING: " .. funcname ..
1249                                             " in syscall_no_abi_change, but pointers args are present")
1250                                 end
1251                                 changes_abi = true
1252                                 goto ptrfound
1253                         end
1254                 end
1255                 ::ptrfound::
1256         end
1257         if config["sys_abi_change"][funcname] then
1258                 changes_abi = true
1259         end
1260         if changes_abi then
1261                 -- argalias should be:
1262                 --   COMPAT_PREFIX + ABI Prefix + funcname
1263                 argprefix = config['abi_func_prefix']
1264                 funcprefix = config['abi_func_prefix']
1265                 funcalias = funcprefix .. funcname
1266                 noproto = false
1267         end
1268         if funcname ~= nil then
1269                 funcname = funcprefix .. funcname
1270         end
1271         if funcalias == nil or funcalias == "" then
1272                 funcalias = funcname
1273         end
1274
1275         if argalias == nil and funcname ~= nil then
1276                 argalias = funcname .. "_args"
1277                 for _, v in pairs(compat_options) do
1278                         local mask = v["mask"]
1279                         if (flags & mask) ~= 0 then
1280                                 -- Multiple aliases doesn't seem to make
1281                                 -- sense.
1282                                 argalias = v["prefix"] .. argalias
1283                                 goto out
1284                         end
1285                 end
1286                 ::out::
1287         elseif argalias ~= nil then
1288                 argalias = argprefix .. argalias
1289         end
1290
1291         local ncompatflags = get_mask({"STD", "NODEF", "NOARGS", "NOPROTO",
1292             "NOSTD"})
1293         local compatflags = get_mask_pat("COMPAT.*")
1294         if noproto or flags & known_flags["SYSMUX"] ~= 0 then
1295                 flags = flags | known_flags["NOPROTO"];
1296         end
1297         if flags & known_flags["OBSOL"] ~= 0 then
1298                 handle_obsol(sysnum, funcname, funcomment)
1299         elseif flags & known_flags["RESERVED"] ~= 0 then
1300                 handle_reserved(sysnum, sysstart, sysend)
1301         elseif flags & known_flags["UNIMPL"] ~= 0 then
1302                 handle_unimpl(sysnum, sysstart, sysend, funcomment)
1303         elseif flags & compatflags ~= 0 then
1304                 if flags & known_flags['STD'] ~= 0 then
1305                         abort(1, "Incompatible COMPAT/STD: " .. line)
1306                 end
1307                 handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
1308                     auditev, funcname, funcalias, funcargs, argalias)
1309         elseif flags & ncompatflags ~= 0 then
1310                 handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
1311                     auditev, syscallret, funcname, funcalias, funcargs,
1312                     argalias)
1313         else
1314                 abort(1, "Bad flags? " .. line)
1315         end
1316
1317         if sysend ~= nil then
1318                 maxsyscall = sysend
1319         elseif sysnum ~= nil then
1320                 maxsyscall = sysnum
1321         end
1322 end
1323
1324 -- Entry point
1325
1326 if #arg < 1 or #arg > 2 then
1327         error("usage: " .. arg[0] .. " input-file <config-file>")
1328 end
1329
1330 local sysfile, configfile = arg[1], arg[2]
1331
1332 -- process_config either returns nil and a message, or a
1333 -- table that we should merge into the global config
1334 if configfile ~= nil then
1335         local res = assert(process_config(configfile))
1336
1337         for k, v in pairs(res) do
1338                 if v ~= config[k] then
1339                         config[k] = v
1340                         config_modified[k] = true
1341                 end
1342         end
1343 end
1344
1345 local compat_set = config['compat_set']
1346 if compat_set ~= "" then
1347         if not compat_option_sets[compat_set] then
1348                 abort(1, "Undefined compat set: " .. compat_set)
1349         end
1350
1351         compat_options = compat_option_sets[compat_set]
1352 else
1353         compat_options = {}
1354 end
1355
1356 -- We ignore errors here if we're relying on the default configuration.
1357 if not config_modified["capenabled"] then
1358         config["capenabled"] = grab_capenabled(config['capabilities_conf'],
1359             config_modified["capabilities_conf"] == nil)
1360 elseif config["capenabled"] ~= "" then
1361         -- Due to limitations in the config format mostly, we'll have a comma
1362         -- separated list.  Parse it into lines
1363         local capenabled = {}
1364         -- print("here: " .. config["capenabled"])
1365         for sysc in config["capenabled"]:gmatch("([^,]+)") do
1366                 capenabled[sysc] = true
1367         end
1368         config["capenabled"] = capenabled
1369 end
1370 process_compat()
1371 process_abi_flags()
1372 process_syscall_abi_change()
1373 process_obsol()
1374 process_unimpl()
1375
1376 if not lfs.mkdir(tmpspace) then
1377         error("Failed to create tempdir " .. tmpspace)
1378 end
1379
1380 -- XXX Revisit the error handling here, we should probably move the rest of this
1381 -- into a function that we pcall() so we can catch the errors and clean up
1382 -- gracefully.
1383 for _, v in ipairs(temp_files) do
1384         local tmpname = tmpspace .. v
1385         files[v] = io.open(tmpname, "w+")
1386         -- XXX Revisit these with a pcall() + error handler
1387         if not files[v] then
1388                 abort(1, "Failed to open temp file: " .. tmpname)
1389         end
1390 end
1391
1392 for _, v in ipairs(output_files) do
1393         local tmpname = tmpspace .. v
1394         files[v] = io.open(tmpname, "w+")
1395         -- XXX Revisit these with a pcall() + error handler
1396         if not files[v] then
1397                 abort(1, "Failed to open temp output file: " .. tmpname)
1398         end
1399 end
1400
1401 -- Write out all of the preamble bits
1402 write_line("sysent", string.format([[
1403
1404 /* The casts are bogus but will do for now. */
1405 struct sysent %s[] = {
1406 ]], config['switchname']))
1407
1408 write_line("syssw", string.format([[/*
1409  * System call switch table.
1410  *
1411  * DO NOT EDIT-- this file is automatically %s.
1412  * $%s$
1413  */
1414
1415 ]], generated_tag, config['os_id_keyword']))
1416
1417 write_line("sysarg", string.format([[/*
1418  * System call prototypes.
1419  *
1420  * DO NOT EDIT-- this file is automatically %s.
1421  * $%s$
1422  */
1423
1424 #ifndef %s
1425 #define %s
1426
1427 #include <sys/signal.h>
1428 #include <sys/acl.h>
1429 #include <sys/cpuset.h>
1430 #include <sys/domainset.h>
1431 #include <sys/_ffcounter.h>
1432 #include <sys/_semaphore.h>
1433 #include <sys/ucontext.h>
1434 #include <sys/wait.h>
1435
1436 #include <bsm/audit_kevents.h>
1437
1438 struct proc;
1439
1440 struct thread;
1441
1442 #define PAD_(t) (sizeof(register_t) <= sizeof(t) ? \
1443                 0 : sizeof(register_t) - sizeof(t))
1444
1445 #if BYTE_ORDER == LITTLE_ENDIAN
1446 #define PADL_(t)        0
1447 #define PADR_(t)        PAD_(t)
1448 #else
1449 #define PADL_(t)        PAD_(t)
1450 #define PADR_(t)        0
1451 #endif
1452
1453 ]], generated_tag, config['os_id_keyword'], config['sysproto_h'],
1454     config['sysproto_h']))
1455 if abi_changes("pair_64bit") then
1456         write_line("sysarg", string.format([[
1457 #if !defined(PAD64_REQUIRED) && !defined(__amd64__)
1458 #define PAD64_REQUIRED
1459 #endif
1460 ]]))
1461 end
1462 if abi_changes("pair_64bit") then
1463         write_line("systrace", string.format([[
1464 #if !defined(PAD64_REQUIRED) && !defined(__amd64__)
1465 #define PAD64_REQUIRED
1466 #endif
1467 ]]))
1468 end
1469 for _, v in pairs(compat_options) do
1470         write_line(v["tmp"], string.format("\n#ifdef %s\n\n", v["definition"]))
1471 end
1472
1473 write_line("sysnames", string.format([[/*
1474  * System call names.
1475  *
1476  * DO NOT EDIT-- this file is automatically %s.
1477  * $%s$
1478  */
1479
1480 const char *%s[] = {
1481 ]], generated_tag, config['os_id_keyword'], config['namesname']))
1482
1483 write_line("syshdr", string.format([[/*
1484  * System call numbers.
1485  *
1486  * DO NOT EDIT-- this file is automatically %s.
1487  * $%s$
1488  */
1489
1490 ]], generated_tag, config['os_id_keyword']))
1491
1492 write_line("sysmk", string.format([[# FreeBSD system call object files.
1493 # DO NOT EDIT-- this file is automatically %s.
1494 # $%s$
1495 MIASM = ]], generated_tag, config['os_id_keyword']))
1496
1497 write_line("systrace", string.format([[/*
1498  * System call argument to DTrace register array converstion.
1499  *
1500  * DO NOT EDIT-- this file is automatically %s.
1501  * $%s$
1502  * This file is part of the DTrace syscall provider.
1503  */
1504
1505 static void
1506 systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
1507 {
1508         int64_t *iarg = (int64_t *)uarg;
1509         int a = 0;
1510         switch (sysnum) {
1511 ]], generated_tag, config['os_id_keyword']))
1512
1513 write_line("systracetmp", [[static void
1514 systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1515 {
1516         const char *p = NULL;
1517         switch (sysnum) {
1518 ]])
1519
1520 write_line("systraceret", [[static void
1521 systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1522 {
1523         const char *p = NULL;
1524         switch (sysnum) {
1525 ]])
1526
1527 -- Processing the sysfile will parse out the preprocessor bits and put them into
1528 -- the appropriate place.  Any syscall-looking lines get thrown into the sysfile
1529 -- buffer, one per line, for later processing once they're all glued together.
1530 process_sysfile(sysfile)
1531
1532 write_line("sysinc",
1533     "\n#define AS(name) (sizeof(struct name) / sizeof(register_t))\n")
1534
1535 for _, v in pairs(compat_options) do
1536         if v["count"] > 0 then
1537                 write_line("sysinc", string.format([[
1538
1539 #ifdef %s
1540 #define %s(n, name) .sy_narg = n, .sy_call = (sy_call_t *)__CONCAT(%s, name)
1541 #else
1542 #define %s(n, name) .sy_narg = 0, .sy_call = (sy_call_t *)nosys
1543 #endif
1544 ]], v["definition"], v["flag"]:lower(), v["prefix"], v["flag"]:lower()))
1545         end
1546
1547         write_line(v["dcltmp"], string.format("\n#endif /* %s */\n\n",
1548             v["definition"]))
1549 end
1550
1551 write_line("sysprotoend", string.format([[
1552
1553 #undef PAD_
1554 #undef PADL_
1555 #undef PADR_
1556
1557 #endif /* !%s */
1558 ]], config["sysproto_h"]))
1559
1560 write_line("sysmk", "\n")
1561 write_line("sysent", "};\n")
1562 write_line("sysnames", "};\n")
1563 -- maxsyscall is the highest seen; MAXSYSCALL should be one higher
1564 write_line("syshdr", string.format("#define\t%sMAXSYSCALL\t%d\n",
1565     config["syscallprefix"], maxsyscall + 1))
1566 write_line("systrace", [[
1567         default:
1568                 *n_args = 0;
1569                 break;
1570         };
1571 }
1572 ]])
1573
1574 write_line("systracetmp", [[
1575         default:
1576                 break;
1577         };
1578         if (p != NULL)
1579                 strlcpy(desc, p, descsz);
1580 }
1581 ]])
1582
1583 write_line("systraceret", [[
1584         default:
1585                 break;
1586         };
1587         if (p != NULL)
1588                 strlcpy(desc, p, descsz);
1589 }
1590 ]])
1591
1592 -- Finish up; output
1593 write_line("syssw", read_file("sysinc"))
1594 write_line("syssw", read_file("sysent"))
1595
1596 write_line("sysproto", read_file("sysarg"))
1597 write_line("sysproto", read_file("sysdcl"))
1598 for _, v in pairs(compat_options) do
1599         write_line("sysproto", read_file(v["tmp"]))
1600         write_line("sysproto", read_file(v["dcltmp"]))
1601 end
1602 write_line("sysproto", read_file("sysaue"))
1603 write_line("sysproto", read_file("sysprotoend"))
1604
1605 write_line("systrace", read_file("systracetmp"))
1606 write_line("systrace", read_file("systraceret"))
1607
1608 for _, v in ipairs(output_files) do
1609         local target = config[v]
1610         if target ~= "/dev/null" then
1611                 local fh = assert(io.open(target, "w+"))
1612                 if fh == nil then
1613                         abort(1, "Failed to open '" .. target .. "'")
1614                 end
1615                 assert(fh:write(read_file(v)))
1616                 assert(fh:close())
1617         end
1618 end
1619
1620 cleanup()