]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/tools/makesyscalls.lua
less: upgrade to v581.2.
[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         mincompat = 0,
60         abi_type_suffix = "",
61         abi_flags = "",
62         abi_flags_mask = 0,
63         ptr_intptr_t_cast = "intptr_t",
64 }
65
66 local config_modified = {}
67 local cleantmp = true
68 local tmpspace = "/tmp/sysent." .. unistd.getpid() .. "/"
69
70 local output_files = {
71         "sysnames",
72         "syshdr",
73         "sysmk",
74         "syssw",
75         "systrace",
76         "sysproto",
77 }
78
79 -- These ones we'll create temporary files for; generation purposes.
80 local temp_files = {
81         "sysaue",
82         "sysdcl",
83         "syscompat",
84         "syscompatdcl",
85         "sysent",
86         "sysinc",
87         "sysarg",
88         "sysprotoend",
89         "systracetmp",
90         "systraceret",
91 }
92
93 -- Opened files
94 local files = {}
95
96 local function cleanup()
97         for _, v in pairs(files) do
98                 v:close()
99         end
100         if cleantmp then
101                 if lfs.dir(tmpspace) then
102                         for fname in lfs.dir(tmpspace) do
103                                 os.remove(tmpspace .. "/" .. fname)
104                         end
105                 end
106
107                 if lfs.attributes(tmpspace) and not lfs.rmdir(tmpspace) then
108                         io.stderr:write("Failed to clean up tmpdir: " ..
109                             tmpspace .. "\n")
110                 end
111         else
112                 io.stderr:write("Temp files left in " .. tmpspace .. "\n")
113         end
114 end
115
116 local function abort(status, msg)
117         io.stderr:write(msg .. "\n")
118         cleanup()
119         os.exit(status)
120 end
121
122 -- Each entry should have a value so we can represent abi flags as a bitmask
123 -- for convenience.  One may also optionally provide an expr; this gets applied
124 -- to each argument type to indicate whether this argument is subject to ABI
125 -- change given the configured flags.
126 local known_abi_flags = {
127         long_size = {
128                 value   = 0x00000001,
129                 expr    = "_Contains[a-z_]*_long_",
130         },
131         time_t_size = {
132                 value   = 0x00000002,
133                 expr    = "_Contains[a-z_]*_timet_/",
134         },
135         pointer_args = {
136                 value   = 0x00000004,
137         },
138         pointer_size = {
139                 value   = 0x00000008,
140                 expr    = "_Contains[a-z_]*_ptr_",
141         },
142 }
143
144 local known_flags = {
145         STD             = 0x00000001,
146         OBSOL           = 0x00000002,
147         RESERVED        = 0x00000004,
148         UNIMPL          = 0x00000008,
149         NODEF           = 0x00000010,
150         NOARGS          = 0x00000020,
151         NOPROTO         = 0x00000040,
152         NOSTD           = 0x00000080,
153         NOTSTATIC       = 0x00000100,
154
155         -- Compat flags start from here.  We have plenty of space.
156 }
157
158 -- All compat_options entries should have five entries:
159 --      definition: The preprocessor macro that will be set for this
160 --      compatlevel: The level this compatibility should be included at.  This
161 --          generally represents the version of FreeBSD that it is compatible
162 --          with, but ultimately it's just the level of mincompat in which it's
163 --          included.
164 --      flag: The name of the flag in syscalls.master.
165 --      prefix: The prefix to use for _args and syscall prototype.  This will be
166 --          used as-is, without "_" or any other character appended.
167 --      descr: The description of this compat option in init_sysent.c comments.
168 -- The special "stdcompat" entry will cause the other five to be autogenerated.
169 local compat_options = {
170         {
171                 definition = "COMPAT_43",
172                 compatlevel = 3,
173                 flag = "COMPAT",
174                 prefix = "o",
175                 descr = "old",
176         },
177         { stdcompat = "FREEBSD4" },
178         { stdcompat = "FREEBSD6" },
179         { stdcompat = "FREEBSD7" },
180         { stdcompat = "FREEBSD10" },
181         { stdcompat = "FREEBSD11" },
182         { stdcompat = "FREEBSD12" },
183 }
184
185 local function trim(s, char)
186         if s == nil then
187                 return nil
188         end
189         if char == nil then
190                 char = "%s"
191         end
192         return s:gsub("^" .. char .. "+", ""):gsub(char .. "+$", "")
193 end
194
195 -- We have to io.popen it, making sure it's properly escaped, and grab the
196 -- output from the handle returned.
197 local function exec(cmd)
198         cmd = cmd:gsub('"', '\\"')
199
200         local shcmd = "/bin/sh -c \"" .. cmd .. "\""
201         local fh = io.popen(shcmd)
202         local output = fh:read("a")
203
204         fh:close()
205         return output
206 end
207
208 -- config looks like a shell script; in fact, the previous makesyscalls.sh
209 -- script actually sourced it in.  It had a pretty common format, so we should
210 -- be fine to make various assumptions
211 local function process_config(file)
212         local cfg = {}
213         local comment_line_expr = "^%s*#.*"
214         -- We capture any whitespace padding here so we can easily advance to
215         -- the end of the line as needed to check for any trailing bogus bits.
216         -- Alternatively, we could drop the whitespace and instead try to
217         -- use a pattern to strip out the meaty part of the line, but then we
218         -- would need to sanitize the line for potentially special characters.
219         local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]+[`\"]?)"
220
221         if file == nil then
222                 return nil, "No file given"
223         end
224
225         local fh = io.open(file)
226         if fh == nil then
227                 return nil, "Could not open file"
228         end
229
230         for nextline in fh:lines() do
231                 -- Strip any whole-line comments
232                 nextline = nextline:gsub(comment_line_expr, "")
233                 -- Parse it into key, value pairs
234                 local key, value = nextline:match(line_expr)
235                 if key ~= nil and value ~= nil then
236                         local kvp = key .. "=" .. value
237                         key = trim(key)
238                         value = trim(value)
239                         local delim = value:sub(1,1)
240                         if delim == '`' or delim == '"' then
241                                 local trailing_context
242                                 -- Strip off the key/value part
243                                 trailing_context = nextline:sub(kvp:len() + 1)
244                                 -- Strip off any trailing comment
245                                 trailing_context = trailing_context:gsub("#.*$",
246                                     "")
247                                 -- Strip off leading/trailing whitespace
248                                 trailing_context = trim(trailing_context)
249                                 if trailing_context ~= "" then
250                                         print(trailing_context)
251                                         abort(1, "Malformed line: " .. nextline)
252                                 end
253                         end
254                         if delim == '`' then
255                                 -- Command substition may use $1 and $2 to mean
256                                 -- the syscall definition file and itself
257                                 -- respectively.  We'll go ahead and replace
258                                 -- $[0-9] with respective arg in case we want to
259                                 -- expand this in the future easily...
260                                 value = trim(value, delim)
261                                 for capture in value:gmatch("$([0-9]+)") do
262                                         capture = tonumber(capture)
263                                         if capture > #arg then
264                                                 abort(1, "Not enough args: " ..
265                                                     value)
266                                         end
267                                         value = value:gsub("$" .. capture,
268                                             arg[capture])
269                                 end
270
271                                 value = exec(value)
272                         elseif delim == '"' then
273                                 value = trim(value, delim)
274                         else
275                                 -- Strip off potential comments
276                                 value = value:gsub("#.*$", "")
277                                 -- Strip off any padding whitespace
278                                 value = trim(value)
279                                 if value:match("%s") then
280                                         abort(1, "Malformed config line: " ..
281                                             nextline)
282                                 end
283                         end
284                         cfg[key] = value
285                 elseif not nextline:match("^%s*$") then
286                         -- Make sure format violations don't get overlooked
287                         -- here, but ignore blank lines.  Comments are already
288                         -- stripped above.
289                         abort(1, "Malformed config line: " .. nextline)
290                 end
291         end
292
293         io.close(fh)
294         return cfg
295 end
296
297 local function grab_capenabled(file, open_fail_ok)
298         local capentries = {}
299         local commentExpr = "#.*"
300
301         if file == nil then
302                 print "No file"
303                 return {}
304         end
305
306         local fh = io.open(file)
307         if fh == nil then
308                 if not open_fail_ok then
309                         abort(1, "Failed to open " .. file)
310                 end
311                 return {}
312         end
313
314         for nextline in fh:lines() do
315                 -- Strip any comments
316                 nextline = nextline:gsub(commentExpr, "")
317                 if nextline ~= "" then
318                         capentries[nextline] = true
319                 end
320         end
321
322         io.close(fh)
323         return capentries
324 end
325
326 local function process_compat()
327         local nval = 0
328         for _, v in pairs(known_flags) do
329                 if v > nval then
330                         nval = v
331                 end
332         end
333
334         nval = nval << 1
335         for _, v in pairs(compat_options) do
336                 if v["stdcompat"] ~= nil then
337                         local stdcompat = v["stdcompat"]
338                         v["definition"] = "COMPAT_" .. stdcompat:upper()
339                         v["compatlevel"] = tonumber(stdcompat:match("([0-9]+)$"))
340                         v["flag"] = stdcompat:gsub("FREEBSD", "COMPAT")
341                         v["prefix"] = stdcompat:lower() .. "_"
342                         v["descr"] = stdcompat:lower()
343                 end
344
345                 local tmpname = "sys" .. v["flag"]:lower()
346                 local dcltmpname = tmpname .. "dcl"
347                 files[tmpname] = io.tmpfile()
348                 files[dcltmpname] = io.tmpfile()
349                 v["tmp"] = tmpname
350                 v["dcltmp"] = dcltmpname
351
352                 known_flags[v["flag"]] = nval
353                 v["mask"] = nval
354                 nval = nval << 1
355
356                 v["count"] = 0
357         end
358 end
359
360 local function process_abi_flags()
361         local flags, mask = config["abi_flags"], 0
362         for txtflag in flags:gmatch("([^|]+)") do
363                 if known_abi_flags[txtflag] == nil then
364                         abort(1, "Unknown abi_flag: " .. txtflag)
365                 end
366
367                 mask = mask | known_abi_flags[txtflag]["value"]
368         end
369
370         config["abi_flags_mask"] = mask
371 end
372
373 local function abi_changes(name)
374         if known_abi_flags[name] == nil then
375                 abort(1, "abi_changes: unknown flag: " .. name)
376         end
377
378         return config["abi_flags_mask"] & known_abi_flags[name]["value"] ~= 0
379 end
380
381 local function strip_abi_prefix(funcname)
382         local abiprefix = config["abi_func_prefix"]
383         local stripped_name
384         if abiprefix ~= "" and funcname:find("^" .. abiprefix) then
385                 stripped_name = funcname:gsub("^" .. abiprefix, "")
386         else
387                 stripped_name = funcname
388         end
389
390         return stripped_name
391 end
392
393 local function read_file(tmpfile)
394         if files[tmpfile] == nil then
395                 print("Not found: " .. tmpfile)
396                 return
397         end
398
399         local fh = files[tmpfile]
400         fh:seek("set")
401         return fh:read("a")
402 end
403
404 local function write_line(tmpfile, line)
405         if files[tmpfile] == nil then
406                 print("Not found: " .. tmpfile)
407                 return
408         end
409         files[tmpfile]:write(line)
410 end
411
412 local function write_line_pfile(tmppat, line)
413         for k in pairs(files) do
414                 if k:match(tmppat) ~= nil then
415                         files[k]:write(line)
416                 end
417         end
418 end
419
420 local function isptrtype(type)
421         return type:find("*") or type:find("caddr_t")
422             -- XXX NOTYET: or type:find("intptr_t")
423 end
424
425 local process_syscall_def
426
427 -- These patterns are processed in order on any line that isn't empty.
428 local pattern_table = {
429         {
430                 pattern = "%s*$" .. config['os_id_keyword'],
431                 process = function(_, _)
432                         -- Ignore... ID tag
433                 end,
434         },
435         {
436                 dump_prevline = true,
437                 pattern = "^#%s*include",
438                 process = function(line)
439                         line = line .. "\n"
440                         write_line('sysinc', line)
441                 end,
442         },
443         {
444                 dump_prevline = true,
445                 pattern = "^#",
446                 process = function(line)
447                         if line:find("^#%s*if") then
448                                 savesyscall = maxsyscall
449                         elseif line:find("^#%s*else") then
450                                 maxsyscall = savesyscall
451                         end
452                         line = line .. "\n"
453                         write_line('sysent', line)
454                         write_line('sysdcl', line)
455                         write_line('sysarg', line)
456                         write_line_pfile('syscompat[0-9]*$', line)
457                         write_line('sysnames', line)
458                         write_line_pfile('systrace.*', line)
459                 end,
460         },
461         {
462                 -- Buffer anything else
463                 pattern = ".+",
464                 process = function(line, prevline)
465                         local incomplete = line:find("\\$") ~= nil
466                         -- Lines that end in \ get the \ stripped
467                         -- Lines that start with a syscall number, prepend \n
468                         line = trim(line):gsub("\\$", "")
469                         if line:find("^[0-9]") and prevline then
470                                 process_syscall_def(prevline)
471                                 prevline = nil
472                         end
473
474                         prevline = (prevline or '') .. line
475                         incomplete = incomplete or prevline:find(",$") ~= nil
476                         incomplete = incomplete or prevline:find("{") ~= nil and
477                             prevline:find("}") == nil
478                         if prevline:find("^[0-9]") and not incomplete then
479                                 process_syscall_def(prevline)
480                                 prevline = nil
481                         end
482
483                         return prevline
484                 end,
485         },
486 }
487
488 local function process_sysfile(file)
489         local capentries = {}
490         local commentExpr = "^%s*;.*"
491
492         if file == nil then
493                 print "No file"
494                 return {}
495         end
496
497         local fh = io.open(file)
498         if fh == nil then
499                 print("Failed to open " .. file)
500                 return {}
501         end
502
503         local function do_match(nextline, prevline)
504                 local pattern, handler, dump
505                 for _, v in pairs(pattern_table) do
506                         pattern = v['pattern']
507                         handler = v['process']
508                         dump = v['dump_prevline']
509                         if nextline:match(pattern) then
510                                 if dump and prevline then
511                                         process_syscall_def(prevline)
512                                         prevline = nil
513                                 end
514
515                                 return handler(nextline, prevline)
516                         end
517                 end
518
519                 abort(1, "Failed to handle: " .. nextline)
520         end
521
522         local prevline
523         for nextline in fh:lines() do
524                 -- Strip any comments
525                 nextline = nextline:gsub(commentExpr, "")
526                 if nextline ~= "" then
527                         prevline = do_match(nextline, prevline)
528                 end
529         end
530
531         -- Dump any remainder
532         if prevline ~= nil and prevline:find("^[0-9]") then
533                 process_syscall_def(prevline)
534         end
535
536         io.close(fh)
537         return capentries
538 end
539
540 local function get_mask(flags)
541         local mask = 0
542         for _, v in ipairs(flags) do
543                 if known_flags[v] == nil then
544                         abort(1, "Checking for unknown flag " .. v)
545                 end
546
547                 mask = mask | known_flags[v]
548         end
549
550         return mask
551 end
552
553 local function get_mask_pat(pflags)
554         local mask = 0
555         for k, v in pairs(known_flags) do
556                 if k:find(pflags) then
557                         mask = mask | v
558                 end
559         end
560
561         return mask
562 end
563
564 local function align_sysent_comment(col)
565         write_line("sysent", "\t")
566         col = col + 8 - col % 8
567         while col < 56 do
568                 write_line("sysent", "\t")
569                 col = col + 8
570         end
571 end
572
573 local function strip_arg_annotations(arg)
574         arg = arg:gsub("_In[^ ]*[_)] ?", "")
575         arg = arg:gsub("_Out[^ ]*[_)] ?", "")
576         return trim(arg)
577 end
578
579 local function check_abi_changes(arg)
580         for k, v in pairs(known_abi_flags) do
581                 local expr = v["expr"]
582                 if abi_changes(k) and expr ~= nil and arg:find(expr) then
583                         return true
584                 end
585         end
586
587         return false
588 end
589
590 local function process_args(args)
591         local funcargs = {}
592
593         for arg in args:gmatch("([^,]+)") do
594                 local abi_change = not isptrtype(arg) or check_abi_changes(arg)
595
596                 arg = strip_arg_annotations(arg)
597
598                 local argname = arg:match("([^* ]+)$")
599
600                 -- argtype is... everything else.
601                 local argtype = trim(arg:gsub(argname .. "$", ""), nil)
602
603                 if argtype == "" and argname == "void" then
604                         goto out
605                 end
606
607                 -- XX TODO: Forward declarations? See: sysstubfwd in CheriBSD
608                 if abi_change then
609                         local abi_type_suffix = config["abi_type_suffix"]
610                         argtype = argtype:gsub("_native ", "")
611                         argtype = argtype:gsub("(struct [^ ]*)", "%1" ..
612                             abi_type_suffix)
613                         argtype = argtype:gsub("(union [^ ]*)", "%1" ..
614                             abi_type_suffix)
615                 end
616
617                 funcargs[#funcargs + 1] = {
618                         type = argtype,
619                         name = argname,
620                 }
621         end
622
623         ::out::
624         return funcargs
625 end
626
627 local function handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
628     auditev, syscallret, funcname, funcalias, funcargs, argalias)
629         local argssize
630
631         if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
632                 argssize = "AS(" .. argalias .. ")"
633         else
634                 argssize = "0"
635         end
636
637         write_line("systrace", string.format([[
638         /* %s */
639         case %d: {
640 ]], funcname, sysnum))
641         write_line("systracetmp", string.format([[
642         /* %s */
643         case %d:
644 ]], funcname, sysnum))
645         write_line("systraceret", string.format([[
646         /* %s */
647         case %d:
648 ]], funcname, sysnum))
649
650         if #funcargs > 0 then
651                 write_line("systracetmp", "\t\tswitch(ndx) {\n")
652                 write_line("systrace", string.format(
653                     "\t\tstruct %s *p = params;\n", argalias))
654
655                 local argtype, argname
656                 for idx, arg in ipairs(funcargs) do
657                         argtype = arg["type"]
658                         argname = arg["name"]
659
660                         argtype = trim(argtype:gsub("__restrict$", ""), nil)
661                         -- Pointer arg?
662                         if argtype:find("*") then
663                                 write_line("systracetmp", string.format(
664                                     "\t\tcase %d:\n\t\t\tp = \"userland %s\";\n\t\t\tbreak;\n",
665                                     idx - 1, argtype))
666                         else
667                                 write_line("systracetmp", string.format(
668                                     "\t\tcase %d:\n\t\t\tp = \"%s\";\n\t\t\tbreak;\n",
669                                     idx - 1, argtype))
670                         end
671
672                         if isptrtype(argtype) then
673                                 write_line("systrace", string.format(
674                                     "\t\tuarg[%d] = (%s) p->%s; /* %s */\n",
675                                     idx - 1, config["ptr_intptr_t_cast"],
676                                     argname, argtype))
677                         elseif argtype == "union l_semun" then
678                                 write_line("systrace", string.format(
679                                     "\t\tuarg[%d] = p->%s.buf; /* %s */\n",
680                                     idx - 1, argname, argtype))
681                         elseif argtype:sub(1,1) == "u" or argtype == "size_t" then
682                                 write_line("systrace", string.format(
683                                     "\t\tuarg[%d] = p->%s; /* %s */\n",
684                                     idx - 1, argname, argtype))
685                         else
686                                 write_line("systrace", string.format(
687                                     "\t\tiarg[%d] = p->%s; /* %s */\n",
688                                     idx - 1, argname, argtype))
689                         end
690                 end
691
692                 write_line("systracetmp",
693                     "\t\tdefault:\n\t\t\tbreak;\n\t\t};\n")
694
695                 write_line("systraceret", string.format([[
696                 if (ndx == 0 || ndx == 1)
697                         p = "%s";
698                 break;
699 ]], syscallret))
700         end
701         write_line("systrace", string.format(
702             "\t\t*n_args = %d;\n\t\tbreak;\n\t}\n", #funcargs))
703         write_line("systracetmp", "\t\tbreak;\n")
704
705         local nargflags = get_mask({"NOARGS", "NOPROTO", "NODEF"})
706         if flags & nargflags == 0 then
707                 if #funcargs > 0 then
708                         write_line("sysarg", string.format("struct %s {\n",
709                             argalias))
710                         for _, v in ipairs(funcargs) do
711                                 local argname, argtype = v["name"], v["type"]
712                                 write_line("sysarg", string.format(
713                                     "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
714                                     argname, argtype,
715                                     argtype, argname,
716                                     argname, argtype))
717                         end
718                         write_line("sysarg", "};\n")
719                 else
720                         write_line("sysarg", string.format(
721                             "struct %s {\n\tregister_t dummy;\n};\n", argalias))
722                 end
723         end
724
725         local protoflags = get_mask({"NOPROTO", "NODEF"})
726         if flags & protoflags == 0 then
727                 if funcname == "nosys" or funcname == "lkmnosys" or
728                     funcname == "sysarch" or funcname:find("^freebsd") or
729                     funcname:find("^linux") or
730                     funcname:find("^cloudabi") then
731                         write_line("sysdcl", string.format(
732                             "%s\t%s(struct thread *, struct %s *)",
733                             rettype, funcname, argalias))
734                 else
735                         write_line("sysdcl", string.format(
736                             "%s\tsys_%s(struct thread *, struct %s *)",
737                             rettype, funcname, argalias))
738                 end
739                 write_line("sysdcl", ";\n")
740                 write_line("sysaue", string.format("#define\t%sAUE_%s\t%s\n",
741                     config['syscallprefix'], funcalias, auditev))
742         end
743
744         write_line("sysent",
745             string.format("\t{ .sy_narg = %s, .sy_call = (sy_call_t *)", argssize))
746         local column = 8 + 2 + #argssize + 15
747
748         if flags & known_flags["NOSTD"] ~= 0 then
749                 write_line("sysent", string.format(
750                     "lkmressys, .sy_auevent = AUE_NULL, " ..
751                     ".sy_flags = %s, .sy_thrcnt = SY_THR_ABSENT },",
752                     sysflags))
753                 column = column + #"lkmressys" + #"AUE_NULL" + 3
754         else
755                 if funcname == "nosys" or funcname == "lkmnosys" or
756                     funcname == "sysarch" or funcname:find("^freebsd") or
757                     funcname:find("^linux") or
758                     funcname:find("^cloudabi") then
759                         write_line("sysent", string.format(
760                             "%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
761                             funcname, auditev, sysflags, thr_flag))
762                         column = column + #funcname + #auditev + #sysflags + 3
763                 else
764                         write_line("sysent", string.format(
765                             "sys_%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
766                             funcname, auditev, sysflags, thr_flag))
767                         column = column + #funcname + #auditev + #sysflags + 7
768                 end
769         end
770
771         align_sysent_comment(column)
772         write_line("sysent", string.format("/* %d = %s */\n",
773             sysnum, funcalias))
774         write_line("sysnames", string.format("\t\"%s\",\t\t\t/* %d = %s */\n",
775             funcalias, sysnum, funcalias))
776
777         if flags & known_flags["NODEF"] == 0 then
778                 write_line("syshdr", string.format("#define\t%s%s\t%d\n",
779                     config['syscallprefix'], funcalias, sysnum))
780                 write_line("sysmk", string.format(" \\\n\t%s.o",
781                     funcalias))
782         end
783 end
784
785 local function handle_obsol(sysnum, funcname, comment)
786         write_line("sysent",
787             "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
788             ".sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT },")
789         align_sysent_comment(34)
790
791         write_line("sysent", string.format("/* %d = obsolete %s */\n",
792             sysnum, comment))
793         write_line("sysnames", string.format(
794             "\t\"obs_%s\",\t\t\t/* %d = obsolete %s */\n",
795             funcname, sysnum, comment))
796         write_line("syshdr", string.format("\t\t\t\t/* %d is obsolete %s */\n",
797             sysnum, comment))
798 end
799
800 local function handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
801     auditev, funcname, funcalias, funcargs, argalias)
802         local argssize, out, outdcl, wrap, prefix, descr
803
804         if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
805                 argssize = "AS(" .. argalias .. ")"
806         else
807                 argssize = "0"
808         end
809
810         for _, v in pairs(compat_options) do
811                 if flags & v["mask"] ~= 0 then
812                         if config["mincompat"] > v["compatlevel"] then
813                                 funcname = strip_abi_prefix(funcname)
814                                 funcname = v["prefix"] .. funcname
815                                 return handle_obsol(sysnum, funcname, funcname)
816                         end
817                         v["count"] = v["count"] + 1
818                         out = v["tmp"]
819                         outdcl = v["dcltmp"]
820                         wrap = v["flag"]:lower()
821                         prefix = v["prefix"]
822                         descr = v["descr"]
823                         goto compatdone
824                 end
825         end
826
827         ::compatdone::
828         local dprotoflags = get_mask({"NOPROTO", "NODEF"})
829         local nargflags = dprotoflags | known_flags["NOARGS"]
830         if #funcargs > 0 and flags & nargflags == 0 then
831                 write_line(out, string.format("struct %s {\n", argalias))
832                 for _, v in ipairs(funcargs) do
833                         local argname, argtype = v["name"], v["type"]
834                         write_line(out, string.format(
835                             "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
836                             argname, argtype,
837                             argtype, argname,
838                             argname, argtype))
839                 end
840                 write_line(out, "};\n")
841         elseif flags & nargflags == 0 then
842                 write_line("sysarg", string.format(
843                     "struct %s {\n\tregister_t dummy;\n};\n", argalias))
844         end
845         if flags & dprotoflags == 0 then
846                 write_line(outdcl, string.format(
847                     "%s\t%s%s(struct thread *, struct %s *);\n",
848                     rettype, prefix, funcname, argalias))
849                 write_line("sysaue", string.format(
850                     "#define\t%sAUE_%s%s\t%s\n", config['syscallprefix'],
851                     prefix, funcname, auditev))
852         end
853
854         if flags & known_flags['NOSTD'] ~= 0 then
855                 write_line("sysent", string.format(
856                     "\t{ .sy_narg = %s, .sy_call = (sy_call_t *)%s, " ..
857                     ".sy_auevent = %s, .sy_flags = 0, " ..
858                     ".sy_thrcnt = SY_THR_ABSENT },",
859                     "0", "lkmressys", "AUE_NULL"))
860                 align_sysent_comment(8 + 2 + #"0" + 15 + #"lkmressys" +
861                     #"AUE_NULL" + 3)
862         else
863                 write_line("sysent", string.format(
864                     "\t{ %s(%s,%s), .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
865                     wrap, argssize, funcname, auditev, sysflags, thr_flag))
866                 align_sysent_comment(8 + 9 + #argssize + 1 + #funcname +
867                     #auditev + #sysflags + 4)
868         end
869
870         write_line("sysent", string.format("/* %d = %s %s */\n",
871             sysnum, descr, funcalias))
872         write_line("sysnames", string.format(
873             "\t\"%s.%s\",\t\t/* %d = %s %s */\n",
874             wrap, funcalias, sysnum, descr, funcalias))
875         -- Do not provide freebsdN_* symbols in libc for < FreeBSD 7
876         local nosymflags = get_mask({"COMPAT", "COMPAT4", "COMPAT6"})
877         if flags & nosymflags ~= 0 then
878                 write_line("syshdr", string.format(
879                     "\t\t\t\t/* %d is %s %s */\n",
880                     sysnum, descr, funcalias))
881         elseif flags & known_flags["NODEF"] == 0 then
882                 write_line("syshdr", string.format("#define\t%s%s%s\t%d\n",
883                     config['syscallprefix'], prefix, funcalias, sysnum))
884                 write_line("sysmk", string.format(" \\\n\t%s%s.o",
885                     prefix, funcalias))
886         end
887 end
888
889 local function handle_unimpl(sysnum, sysstart, sysend, comment)
890         if sysstart == nil and sysend == nil then
891                 sysstart = tonumber(sysnum)
892                 sysend = tonumber(sysnum)
893         end
894
895         sysnum = sysstart
896         while sysnum <= sysend do
897                 write_line("sysent", string.format(
898                     "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
899                     ".sy_auevent = AUE_NULL, .sy_flags = 0, " ..
900                     ".sy_thrcnt = SY_THR_ABSENT },\t\t\t/* %d = %s */\n",
901                     sysnum, comment))
902                 write_line("sysnames", string.format(
903                     "\t\"#%d\",\t\t\t/* %d = %s */\n",
904                     sysnum, sysnum, comment))
905                 sysnum = sysnum + 1
906         end
907 end
908
909 local function handle_reserved(sysnum, sysstart, sysend, comment)
910         handle_unimpl(sysnum, sysstart, sysend, "reserved for local use")
911 end
912
913 process_syscall_def = function(line)
914         local sysstart, sysend, flags, funcname, sysflags
915         local thr_flag, syscallret
916         local orig = line
917         flags = 0
918         thr_flag = "SY_THR_STATIC"
919
920         -- Parse out the interesting information first
921         local initialExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s*"
922         local sysnum, auditev, allflags = line:match(initialExpr)
923
924         if sysnum == nil or auditev == nil or allflags == nil then
925                 -- XXX TODO: Better?
926                 abort(1, "Completely malformed: " .. line)
927         end
928
929         if sysnum:find("-") then
930                 sysstart, sysend = sysnum:match("^([%d]+)-([%d]+)$")
931                 if sysstart == nil or sysend == nil then
932                         abort(1, "Malformed range: " .. sysnum)
933                 end
934                 sysnum = nil
935                 sysstart = tonumber(sysstart)
936                 sysend = tonumber(sysend)
937                 if sysstart ~= maxsyscall + 1 then
938                         abort(1, "syscall number out of sync, missing " ..
939                             maxsyscall + 1)
940                 end
941         else
942                 sysnum = tonumber(sysnum)
943                 if sysnum ~= maxsyscall + 1 then
944                         abort(1, "syscall number out of sync, missing " ..
945                             maxsyscall + 1)
946                 end
947         end
948
949         -- Split flags
950         for flag in allflags:gmatch("([^|]+)") do
951                 if known_flags[flag] == nil then
952                         abort(1, "Unknown flag " .. flag .. " for " ..  sysnum)
953                 end
954                 flags = flags | known_flags[flag]
955         end
956
957         if (flags & get_mask({"RESERVED", "UNIMPL"})) == 0 and sysnum == nil then
958                 abort(1, "Range only allowed with RESERVED and UNIMPL: " .. line)
959         end
960
961         if (flags & known_flags["NOTSTATIC"]) ~= 0 then
962                 thr_flag = "SY_THR_ABSENT"
963         end
964
965         -- Strip earlier bits out, leave declaration + alt
966         line = line:gsub("^.+" .. allflags .. "%s*", "")
967
968         local decl_fnd = line:find("^{") ~= nil
969         if decl_fnd and line:find("}") == nil then
970                 abort(1, "Malformed, no closing brace: " .. line)
971         end
972
973         local decl, alt
974         if decl_fnd then
975                 line = line:gsub("^{", "")
976                 decl, alt = line:match("([^}]*)}[%s]*(.*)$")
977         else
978                 alt = line
979         end
980
981         if decl == nil and alt == nil then
982                 abort(1, "Malformed bits: " .. line)
983         end
984
985         local funcalias, funcomment, argalias, rettype, args
986         if not decl_fnd and alt ~= nil and alt ~= "" then
987                 -- Peel off one entry for name
988                 funcname = trim(alt:match("^([^%s]+)"), nil)
989                 alt = alt:gsub("^([^%s]+)[%s]*", "")
990         end
991         -- Do we even need it?
992         if flags & get_mask({"OBSOL", "UNIMPL"}) ~= 0 then
993                 local NF = 0
994                 for _ in orig:gmatch("[^%s]+") do
995                         NF = NF + 1
996                 end
997
998                 funcomment = funcname or ''
999                 if NF < 6 then
1000                         funcomment = funcomment .. " " .. alt
1001                 end
1002
1003                 funcomment = trim(funcomment)
1004
1005 --              if funcname ~= nil then
1006 --              else
1007 --                      funcomment = trim(alt)
1008 --              end
1009                 goto skipalt
1010         end
1011
1012         if alt ~= nil and alt ~= "" then
1013                 local altExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)"
1014                 funcalias, argalias, rettype = alt:match(altExpr)
1015                 funcalias = trim(funcalias)
1016                 if funcalias == nil or argalias == nil or rettype == nil then
1017                         abort(1, "Malformed alt: " .. line)
1018                 end
1019         end
1020         if decl_fnd then
1021                 -- Don't clobber rettype set in the alt information
1022                 if rettype == nil then
1023                         rettype = "int"
1024                 end
1025                 -- Peel off the return type
1026                 syscallret = line:match("([^%s]+)%s")
1027                 line = line:match("[^%s]+%s(.+)")
1028                 -- Pointer incoming
1029                 if line:sub(1,1) == "*" then
1030                         syscallret = syscallret .. " "
1031                 end
1032                 while line:sub(1,1) == "*" do
1033                         line = line:sub(2)
1034                         syscallret = syscallret .. "*"
1035                 end
1036                 funcname = line:match("^([^(]+)%(")
1037                 if funcname == nil then
1038                         abort(1, "Not a signature? " .. line)
1039                 end
1040                 args = line:match("^[^(]+%((.+)%)[^)]*$")
1041                 args = trim(args, '[,%s]')
1042         end
1043
1044         ::skipalt::
1045
1046         if funcname == nil then
1047                 funcname = funcalias
1048         end
1049
1050         funcname = trim(funcname)
1051
1052         sysflags = "0"
1053
1054         -- NODEF events do not get audited
1055         if flags & known_flags['NODEF'] ~= 0 then
1056                 auditev = 'AUE_NULL'
1057         end
1058
1059         -- If applicable; strip the ABI prefix from the name
1060         local stripped_name = strip_abi_prefix(funcname)
1061
1062         if config["capenabled"][funcname] ~= nil or
1063             config["capenabled"][stripped_name] ~= nil then
1064                 sysflags = "SYF_CAPENABLED"
1065         end
1066
1067         local funcargs = {}
1068         if args ~= nil then
1069                 funcargs = process_args(args)
1070         end
1071
1072         local argprefix = ''
1073         if abi_changes("pointer_args") then
1074                 for _, v in ipairs(funcargs) do
1075                         if isptrtype(v["type"]) then
1076                                 -- argalias should be:
1077                                 --   COMPAT_PREFIX + ABI Prefix + funcname
1078                                 argprefix = config['abi_func_prefix']
1079                                 funcalias = config['abi_func_prefix'] ..
1080                                     funcname
1081                                 goto ptrfound
1082                         end
1083                 end
1084                 ::ptrfound::
1085         end
1086         if funcalias == nil or funcalias == "" then
1087                 funcalias = funcname
1088         end
1089
1090         if argalias == nil and funcname ~= nil then
1091                 argalias = argprefix .. funcname .. "_args"
1092                 for _, v in pairs(compat_options) do
1093                         local mask = v["mask"]
1094                         if (flags & mask) ~= 0 then
1095                                 -- Multiple aliases doesn't seem to make
1096                                 -- sense.
1097                                 argalias = v["prefix"] .. argalias
1098                                 goto out
1099                         end
1100                 end
1101                 ::out::
1102         elseif argalias ~= nil then
1103                 argalias = argprefix .. argalias
1104         end
1105
1106         local ncompatflags = get_mask({"STD", "NODEF", "NOARGS", "NOPROTO",
1107             "NOSTD"})
1108         local compatflags = get_mask_pat("COMPAT.*")
1109         -- Now try compat...
1110         if flags & compatflags ~= 0 then
1111                 if flags & known_flags['STD'] ~= 0 then
1112                         abort(1, "Incompatible COMPAT/STD: " .. line)
1113                 end
1114                 handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
1115                     auditev, funcname, funcalias, funcargs, argalias)
1116         elseif flags & ncompatflags ~= 0 then
1117                 handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
1118                     auditev, syscallret, funcname, funcalias, funcargs,
1119                     argalias)
1120         elseif flags & known_flags["OBSOL"] ~= 0 then
1121                 handle_obsol(sysnum, funcname, funcomment)
1122         elseif flags & known_flags["RESERVED"] ~= 0 then
1123                 handle_reserved(sysnum, sysstart, sysend)
1124         elseif flags & known_flags["UNIMPL"] ~= 0 then
1125                 handle_unimpl(sysnum, sysstart, sysend, funcomment)
1126         else
1127                 abort(1, "Bad flags? " .. line)
1128         end
1129
1130         if sysend ~= nil then
1131                 maxsyscall = sysend
1132         elseif sysnum ~= nil then
1133                 maxsyscall = sysnum
1134         end
1135 end
1136
1137 -- Entry point
1138
1139 if #arg < 1 or #arg > 2 then
1140         abort(1, "usage: " .. arg[0] .. " input-file <config-file>")
1141 end
1142
1143 local sysfile, configfile = arg[1], arg[2]
1144
1145 -- process_config either returns nil and a message, or a
1146 -- table that we should merge into the global config
1147 if configfile ~= nil then
1148         local res, msg = process_config(configfile)
1149
1150         if res == nil then
1151                 -- Error... handle?
1152                 print(msg)
1153                 os.exit(1)
1154         end
1155
1156         for k, v in pairs(res) do
1157                 if v ~= config[k] then
1158                         config[k] = v
1159                         config_modified[k] = true
1160                 end
1161         end
1162 end
1163
1164 -- We ignore errors here if we're relying on the default configuration.
1165 if not config_modified["capenabled"] then
1166         config["capenabled"] = grab_capenabled(config['capabilities_conf'],
1167             config_modified["capabilities_conf"] == nil)
1168 elseif config["capenabled"] ~= "" then
1169         -- Due to limitations in the config format mostly, we'll have a comma
1170         -- separated list.  Parse it into lines
1171         local capenabled = {}
1172         -- print("here: " .. config["capenabled"])
1173         for sysc in config["capenabled"]:gmatch("([^,]+)") do
1174                 capenabled[sysc] = true
1175         end
1176         config["capenabled"] = capenabled
1177 end
1178 process_compat()
1179 process_abi_flags()
1180
1181 if not lfs.mkdir(tmpspace) then
1182         abort(1, "Failed to create tempdir " .. tmpspace)
1183 end
1184
1185 for _, v in ipairs(temp_files) do
1186         local tmpname = tmpspace .. v
1187         files[v] = io.open(tmpname, "w+")
1188 end
1189
1190 for _, v in ipairs(output_files) do
1191         local tmpname = tmpspace .. v
1192         files[v] = io.open(tmpname, "w+")
1193 end
1194
1195 -- Write out all of the preamble bits
1196 write_line("sysent", string.format([[
1197
1198 /* The casts are bogus but will do for now. */
1199 struct sysent %s[] = {
1200 ]], config['switchname']))
1201
1202 write_line("syssw", string.format([[/*
1203  * System call switch table.
1204  *
1205  * DO NOT EDIT-- this file is automatically %s.
1206  * $%s$
1207  */
1208
1209 ]], generated_tag, config['os_id_keyword']))
1210
1211 write_line("sysarg", string.format([[/*
1212  * System call prototypes.
1213  *
1214  * DO NOT EDIT-- this file is automatically %s.
1215  * $%s$
1216  */
1217
1218 #ifndef %s
1219 #define %s
1220
1221 #include <sys/signal.h>
1222 #include <sys/acl.h>
1223 #include <sys/cpuset.h>
1224 #include <sys/domainset.h>
1225 #include <sys/_ffcounter.h>
1226 #include <sys/_semaphore.h>
1227 #include <sys/ucontext.h>
1228 #include <sys/wait.h>
1229
1230 #include <bsm/audit_kevents.h>
1231
1232 struct proc;
1233
1234 struct thread;
1235
1236 #define PAD_(t) (sizeof(register_t) <= sizeof(t) ? \
1237                 0 : sizeof(register_t) - sizeof(t))
1238
1239 #if BYTE_ORDER == LITTLE_ENDIAN
1240 #define PADL_(t)        0
1241 #define PADR_(t)        PAD_(t)
1242 #else
1243 #define PADL_(t)        PAD_(t)
1244 #define PADR_(t)        0
1245 #endif
1246
1247 ]], generated_tag, config['os_id_keyword'], config['sysproto_h'],
1248     config['sysproto_h']))
1249 for _, v in pairs(compat_options) do
1250         write_line(v["tmp"], string.format("\n#ifdef %s\n\n", v["definition"]))
1251 end
1252
1253 write_line("sysnames", string.format([[/*
1254  * System call names.
1255  *
1256  * DO NOT EDIT-- this file is automatically %s.
1257  * $%s$
1258  */
1259
1260 const char *%s[] = {
1261 ]], generated_tag, config['os_id_keyword'], config['namesname']))
1262
1263 write_line("syshdr", string.format([[/*
1264  * System call numbers.
1265  *
1266  * DO NOT EDIT-- this file is automatically %s.
1267  * $%s$
1268  */
1269
1270 ]], generated_tag, config['os_id_keyword']))
1271
1272 write_line("sysmk", string.format([[# FreeBSD system call object files.
1273 # DO NOT EDIT-- this file is automatically %s.
1274 # $%s$
1275 MIASM = ]], generated_tag, config['os_id_keyword']))
1276
1277 write_line("systrace", string.format([[/*
1278  * System call argument to DTrace register array converstion.
1279  *
1280  * DO NOT EDIT-- this file is automatically %s.
1281  * $%s$
1282  * This file is part of the DTrace syscall provider.
1283  */
1284
1285 static void
1286 systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
1287 {
1288         int64_t *iarg  = (int64_t *) uarg;
1289         switch (sysnum) {
1290 ]], generated_tag, config['os_id_keyword']))
1291
1292 write_line("systracetmp", [[static void
1293 systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1294 {
1295         const char *p = NULL;
1296         switch (sysnum) {
1297 ]])
1298
1299 write_line("systraceret", [[static void
1300 systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1301 {
1302         const char *p = NULL;
1303         switch (sysnum) {
1304 ]])
1305
1306 -- Processing the sysfile will parse out the preprocessor bits and put them into
1307 -- the appropriate place.  Any syscall-looking lines get thrown into the sysfile
1308 -- buffer, one per line, for later processing once they're all glued together.
1309 process_sysfile(sysfile)
1310
1311 write_line("sysinc",
1312     "\n#define AS(name) (sizeof(struct name) / sizeof(register_t))\n")
1313
1314 for _, v in pairs(compat_options) do
1315         if v["count"] > 0 then
1316                 write_line("sysinc", string.format([[
1317
1318 #ifdef %s
1319 #define %s(n, name) .sy_narg = n, .sy_call = (sy_call_t *)__CONCAT(%s,name)
1320 #else
1321 #define %s(n, name) .sy_narg = 0, .sy_call = (sy_call_t *)nosys
1322 #endif
1323 ]], v["definition"], v["flag"]:lower(), v["prefix"], v["flag"]:lower()))
1324         end
1325
1326         write_line(v["dcltmp"], string.format("\n#endif /* %s */\n\n",
1327             v["definition"]))
1328 end
1329
1330 write_line("sysprotoend", string.format([[
1331
1332 #undef PAD_
1333 #undef PADL_
1334 #undef PADR_
1335
1336 #endif /* !%s */
1337 ]], config["sysproto_h"]))
1338
1339 write_line("sysmk", "\n")
1340 write_line("sysent", "};\n")
1341 write_line("sysnames", "};\n")
1342 -- maxsyscall is the highest seen; MAXSYSCALL should be one higher
1343 write_line("syshdr", string.format("#define\t%sMAXSYSCALL\t%d\n",
1344     config["syscallprefix"], maxsyscall + 1))
1345 write_line("systrace", [[
1346         default:
1347                 *n_args = 0;
1348                 break;
1349         };
1350 }
1351 ]])
1352
1353 write_line("systracetmp", [[
1354         default:
1355                 break;
1356         };
1357         if (p != NULL)
1358                 strlcpy(desc, p, descsz);
1359 }
1360 ]])
1361
1362 write_line("systraceret", [[
1363         default:
1364                 break;
1365         };
1366         if (p != NULL)
1367                 strlcpy(desc, p, descsz);
1368 }
1369 ]])
1370
1371 -- Finish up; output
1372 write_line("syssw", read_file("sysinc"))
1373 write_line("syssw", read_file("sysent"))
1374
1375 write_line("sysproto", read_file("sysarg"))
1376 write_line("sysproto", read_file("sysdcl"))
1377 for _, v in pairs(compat_options) do
1378         write_line("sysproto", read_file(v["tmp"]))
1379         write_line("sysproto", read_file(v["dcltmp"]))
1380 end
1381 write_line("sysproto", read_file("sysaue"))
1382 write_line("sysproto", read_file("sysprotoend"))
1383
1384 write_line("systrace", read_file("systracetmp"))
1385 write_line("systrace", read_file("systraceret"))
1386
1387 for _, v in ipairs(output_files) do
1388         local target = config[v]
1389         if target ~= "/dev/null" then
1390                 local fh = io.open(target, "w+")
1391                 if fh == nil then
1392                         abort(1, "Failed to open '" .. target .. "'")
1393                 end
1394                 fh:write(read_file(v))
1395                 fh:close()
1396         end
1397 end
1398
1399 cleanup()