1 /**
2  * This file is part of the Amalthea library.
3  * Copyright (C) 2018-2024 Eugene 'Vindex' Stulin
4  * Distributed under the BSL 1.0 or (at your option) the GNU LGPL 3.0 or later.
5  *
6  * The module provides mostly wrappers for low-level system functions,
7  * including functions for mounting/unmounting file systems, functions for
8  * working with loop devices, functions for getting information about users
9  * and groups, functions for mappings, for working with signals, etc.
10  */
11 
12 module amalthea.sys;
13 
14 import core.stdc.stdlib;
15 import core.sys.posix.pwd;
16 import core.sys.posix.grp;
17 import core.sys.posix.sys.ioctl : ioctl;
18 import unix_mman = core.sys.posix.sys.mman;
19 import linux_mman = core.sys.linux.sys.mman;
20 import unix_fcntl = core.sys.posix.fcntl;
21 import unix_unistd = core.sys.posix.unistd;
22 import core.stdc.errno;
23 import core.sys.posix.grp : group, getgrnam;
24 import unix_sig = core.sys.posix.signal;
25 import stdc_sig = core.stdc.signal;
26 
27 public import amalthea.libcore;
28 import amalthea.fs;
29 import amalthea.langlocal;
30 import amalthea.terminal;
31 
32 public import amalthea.time : getTimeString;
33 
34 alias isTTY = amalthea.terminal.isTTY;
35 alias isTerminal = amalthea.terminal.isTerminal;
36 
37 import
38     std.array,
39     std.path,
40     std.process,
41     std.range,
42     std.string,
43     std.typecons;
44 
45 import std.file : FileException;
46 
47 
48 /*******************************************************************************
49  * Common function for calling external programs.
50  */
51 int c_system(string cmd) {
52     return core.stdc.stdlib.system(cmd.toStringz);
53 }
54 
55 
56 /*******************************************************************************
57  * Returns file path to the application (searches among system directories).
58  */
59 string getAppPath(string app) {
60     string[] appsRootPaths = environment["PATH"].split(":");
61     string appPath;
62     foreach(p; appsRootPaths) {
63         appPath = std.path.buildPath(p, app);
64         if (amalthea.fs.exists(appPath)) {
65             return appPath;
66         }
67     }
68     return "";
69 }
70 unittest {
71     assert(getAppPath("gcc") == "/usr/bin/gcc");
72     assert(getAppPath("strange_program") == "");
73 }
74 
75 
76 extern(C) private int mkfifo(const char* pathname, uint mode);
77 
78 /*******************************************************************************
79  * The function creates named pipe.
80  * Params:
81  *     pipepath = Name for pipe file.
82  *     mode     = Specifies the FIFO's permission.
83  * See: `man 3 mkfifo`
84  */
85 void createPipe(in string pipepath, uint mode = octal!666) {
86     if (amalthea.fs.exists(pipepath)) {
87         enforce(
88             FileStat(pipepath).type == FileType.pipe,
89             new FileException("File exists and is not a named pipe.")
90         );
91         remove(pipepath);
92     }
93     if (-1 == mkfifo(pipepath.toStringz, mode)) {
94         throw new FileException("mkfifo()");
95     }
96 }
97 
98 
99 /*******************************************************************************
100  * Helper function for getting the standard path to the share directory
101  * of the current program.
102  */
103 string getAppShareDir() {
104     string currentApp = readLink("/proc/self/exe");
105     string shareDir = buildNormalizedPath(dirName(currentApp), "..", "share");
106     return shareDir;
107 }
108 
109 
110 /*******************************************************************************
111  * Helper function for getting the standard path to the help directory
112  * of the current program.
113  */
114 string getAppHelpDir() {
115     string shareDir = getAppShareDir();
116     string helpDir = buildNormalizedPath(shareDir, "help");
117     return helpDir;
118 }
119 
120 
121 /*******************************************************************************
122  * This function returns help text of the current program using
123  * standard paths for help file.
124  */
125 string readAppHelpText(string app, string lang = "") {
126     if (lang == "") {
127         lang = amalthea.langlocal.getSystemLanguage();
128     }
129     immutable helpDir = getAppHelpDir();
130     string defaultHelpFile = buildPath(helpDir, lang, app, "help.txt");
131     string englishHelpFile = buildPath(helpDir, "en_US", app, "help.txt");
132     if (amalthea.fs.exists(defaultHelpFile)) {
133         return readText(defaultHelpFile);
134     } else if (amalthea.fs.exists(englishHelpFile)) {
135         return readText(englishHelpFile);
136     }
137     throw new FileException("Help file doesn't exist.");
138 }
139 
140 
141 /*******************************************************************************
142  * The function returns user name by user ID.
143  */
144 string getNameByUID(uint uid) {
145     auto p = getpwuid(uid);
146     if (p is null) {
147         return uid.to!string;
148     }
149     return (*p).pw_name.fromStringz.to!string;
150 }
151 
152 
153 /*******************************************************************************
154  * The function returns group name by group ID.
155  */
156 string getNameByGID(uint gid) {
157     auto p = getgrgid(gid);
158     if (p is null) {
159         return gid.to!string;
160     }
161     return (*p).gr_name.fromStringz.to!string;
162 }
163 
164 
165 private extern(C)
166 int mount(
167     const char* source,
168     const char* target,
169     const char* filesystemtype,
170     ulong mountflags,
171     const void* data
172 );
173 
174 
175 private extern(C)
176 int umount(const char* source);
177 
178 
179 /*******************************************************************************
180  * Attaches the filesystem specified by source.
181  * Params:
182  *     source = Filesystem source.
183  *     target = Location for mounting.
184  *     fs     = Filesystem type,
185  *              supported values are listed in "/proc/filesystems".
186  *     flags  = Additional mount flags. Zero, by default.
187  *     data   = Special data for required filesystem. Null pointer, by default.
188  *
189  * See also: `man 2 mount`
190  */
191 void mount(
192     string source, string target, string fs, ulong flags=0, void* data=null
193 ) {
194     int ret = mount(
195         source.toStringz, target.toStringz, fs.toStringz, flags, data
196     );
197     enforce(-1 != ret, new FileException("mount"));
198 }
199 
200 
201 /*******************************************************************************
202  * Removes the attachment of the filesystem mounted on target.
203  * Params:
204  *     target = Target directory with mounted filesystem.
205  *
206  * See also: `man 2 umount`
207  */
208 void umount(string target) {
209     enforce(-1 != umount(target.toStringz), new FileException("umount"));
210 }
211 
212 
213 /* Mount flags. */
214 enum MS_RDONLY      = 1;        //< Mount read-only.
215 enum MS_NOSUID      = 2;        //< Ignore suid and sgid bits.
216 enum MS_NODEV       = 4;        //< Disallow access to device special files.
217 enum MS_NOEXEC      = 8;        //< Disallow program execution.
218 enum MS_SYNCHRONOUS = 16;       //< Writes are synced at once.
219 enum MS_REMOUNT     = 32;       //< Alter flags of a mounted FS.
220 enum MS_MANDLOCK    = 64;       //< Allow mandatory locks on an FS.
221 enum MS_DIRSYNC     = 128;      //< Directory modifications are synchronous.
222 enum MS_NOSYMFOLLOW = 256;      //< Do not follow symlinks.
223 enum MS_NOATIME     = 1024;     //< Do not update access times.
224 enum MS_NODIRATIME  = 2048;     //< Do not update directory access times.
225 enum MS_BIND        = 4096;     //< Bind directory at different place.
226 enum MS_MOVE        = 8192;
227 enum MS_REC         = 16_384;
228 enum MS_SILENT      = 32_768;
229 enum MS_POSIXACL    = 1 << 16;  //< VFS does not apply the umask.
230 enum MS_UNBINDABLE  = 1 << 17;  //< Change to unbindable.
231 enum MS_PRIVATE     = 1 << 18;  //< Change to private.
232 enum MS_SLAVE       = 1 << 19;  //< Change to slave.
233 enum MS_SHARED      = 1 << 20;  //< Change to shared.
234 enum MS_RELATIME    = 1 << 21;  //< Update atime relative to mtime/ctime.
235 enum MS_KERNMOUNT   = 1 << 22;  //< This is a kern_mount call.
236 enum MS_I_VERSION   = 1 << 23;  //< Update inode I_version field.
237 enum MS_STRICTATIME = 1 << 24;  //< Always perform atime updates.
238 enum MS_LAZYTIME    = 1 << 25;  //< Update the on-disk [acm]times lazily.
239 enum MS_ACTIVE      = 1 << 30;
240 enum MS_NOUSER      = 1 << 31;
241 
242 
243 private enum LO_NAME_SIZE = 64;
244 private enum LO_KEY_SIZE = 64;
245 
246 
247 struct loop_info64 {
248     ulong lo_device;           // ioctl r/o
249     ulong lo_inode;            // ioctl r/o
250     ulong lo_rdevice;          // ioctl r/o
251     ulong lo_offset;
252     ulong lo_sizelimit;        // bytes, 0 == max available
253     uint  lo_number;           // ioctl r/o
254     uint  lo_encrypt_type;     // obsolete, ignored
255     uint  lo_encrypt_key_size; // ioctl w/o
256     uint  lo_flags;
257     ubyte[LO_NAME_SIZE] lo_file_name;
258     ubyte[LO_NAME_SIZE] lo_crypt_name;
259     ubyte[LO_KEY_SIZE] lo_encrypt_key; // ioctl w/o
260     ulong[2] lo_init;
261 }
262 
263 
264 /*******************************************************************************
265  * Set of functions for loop device management.
266  * See also: `man 4 loop`
267  */
268 class LoopDevControl {
269 
270     /***************************************************************************
271      * Creates loop_info64 structure for loop device with required number.
272      *
273      * Params:
274      *     loopNumber = Loop device number.
275      *
276      * Returns:
277      *     loop_info64 structure object.
278      */
279     static loop_info64 getLoopStatus(long loopNumber) {
280         string loopDevicePath = format!"/dev/loop%s"(loopNumber);
281         int fd = unix_fcntl.open(loopDevicePath.toStringz, unix_fcntl.O_RDWR);
282         enforce(-1 != fd, new FileException(loopDevicePath));
283         loop_info64 info;
284         int ret = ioctl(fd, LOOP_GET_STATUS64, &info);
285         enforce(-1 != ret, new FileException("ioctl, " ~ loopDevicePath));
286         return info;
287     }
288 
289     /***************************************************************************
290      * Returns true if loop device exists, but busy.
291      *
292      * Params:
293      *     loopNumber = Loop device number.
294      *
295      * Returns:
296      *     true or false.
297      */
298     static bool doesLoopDeviceExistAndIsBusy(uint loopNumber) {
299         string loopDevicePath = format!"/dev/loop%s"(loopNumber);
300         if (!amalthea.fs.exists(loopDevicePath)) {
301             return false;
302         }
303         return isLoopDeviceBusy(loopNumber);
304     }
305 
306     /***************************************************************************
307      * Returns true if loop device is busy.
308      *
309      * Params:
310      *     loopNumber = Loop device number.
311      *
312      * Returns:
313      *     true or false.
314      */
315     static bool isLoopDeviceBusy(uint loopNumber) {
316         string loopDevicePath = format!"/dev/loop%s"(loopNumber);
317         int fd = unix_fcntl.open(loopDevicePath.toStringz, unix_fcntl.O_RDWR);
318         enforce(-1 != fd, new FileException(loopDevicePath));
319         loop_info64 info;
320         errno = 0;
321         int ret = ioctl(fd, LOOP_GET_STATUS64, &info);
322         return ret != -1 && errno != ENXIO;
323     }
324 
325     /***************************************************************************
326      * Allocates or finds a free loop device for use.
327      *
328      * Returns:
329      *     free loop device number.
330      */
331     static uint getFreeLoopDevice() {
332         int ctlFd = unix_fcntl.open(
333             LOOP_CONTROL_FILE.toStringz, unix_fcntl.O_RDWR
334         );
335         enforce(-1 != ctlFd, new FileException(LOOP_CONTROL_FILE));
336         scope(exit) unix_unistd.close(ctlFd);
337         int devNumber = ioctl(ctlFd, LOOP_CTL_GET_FREE);
338         enforce(devNumber >= 0, new FileException("ioctl"));
339         return cast(uint)devNumber;
340     }
341 
342     /***************************************************************************
343      * Adds the new loop device.
344      *
345      * Params:
346      *     loopNumber = Loop device number.
347      */
348     static void addLoopDevice(ulong loopNumber) {
349         loopIo(loopNumber, LOOP_CTL_ADD);
350     }
351 
352     /***************************************************************************
353      * Associate the loop device with the required file.
354      *
355      * Params:
356      *     filePath = File path to associate.
357      *     loopNumber = Loop device number.
358      */
359     static void associateFileWithDevice(string filePath, ulong loopNumber) {
360         associatedFileControl(filePath, loopNumber, LOOP_SET_FD);
361     }
362 
363     /***************************************************************************
364      * Associate the loop device with the required file.
365      *
366      * Params:
367      *     filePath = File path to associate.
368      *     loopDevice = Loop device path.
369      */
370     static void associateFileWithDevice(string filePath, string loopDevice) {
371         associatedFileControl(filePath, loopDevice, LOOP_SET_FD);
372     }
373 
374 
375     /***************************************************************************
376      * Disassociate the loop device with the required file.
377      *
378      * Params:
379      *     filePath = Associated file.
380      *     loopNumber = Loop device number.
381      */
382     static void disassociateFileWithDevice(string filePath, ulong loopNumber) {
383         associatedFileControl(filePath, loopNumber, LOOP_CLR_FD);
384     }
385 
386     /***************************************************************************
387      * Disassociate the loop device with the required file.
388      *
389      * Params:
390      *     filePath = Associated file.
391      *     loopDevice = Loop device path.
392      */
393     static void disassociateFileWithDevice(string filePath, string loopDevice) {
394         associatedFileControl(filePath, loopDevice, LOOP_CLR_FD);
395     }
396 
397 private static:
398 
399     extern(C) {
400         enum LOOP_SET_FD = 0x4C00;
401         enum LOOP_CLR_FD = 0x4C01;
402         enum LOOP_SET_STATUS = 0x4C02;
403         enum LOOP_GET_STATUS = 0x4C03;
404         enum LOOP_SET_STATUS64 = 0x4C04;
405         enum LOOP_GET_STATUS64 = 0x4C05;
406         enum LOOP_CHANGE_FD = 0x4C06;
407         enum LOOP_SET_CAPACITY = 0x4C07;
408         enum LOOP_SET_DIRECT_IO = 0x4C08;
409         enum LOOP_SET_BLOCK_SIZE = 0x4C09;
410         enum LOOP_CONFIGURE = 0x4C0A;
411 
412         enum LOOP_CTL_ADD = 0x4C80;
413         enum LOOP_CTL_REMOVE = 0x4C81;
414         enum LOOP_CTL_GET_FREE = 0x4C82;
415     }
416 
417     immutable LOOP_CONTROL_FILE = "/dev/loop-control";
418 
419     void loopIo(ulong number, int ioctlFlag) {
420         int ctlFd = unix_fcntl.open(
421             LOOP_CONTROL_FILE.toStringz, unix_fcntl.O_RDWR
422         );
423         enforce(-1 != ctlFd, new FileException(LOOP_CONTROL_FILE));
424         scope(exit) unix_unistd.close(ctlFd);
425         int devNumber = ioctl(ctlFd, ioctlFlag, number);
426         auto errMsg = format!"loop device %s, ioctl error"(number);
427         enforce(devNumber >= 0, new FileException(errMsg));
428     }
429 
430     void associatedFileControl(string filePath, ulong loopNumber, int flag) {
431         string loopDevice = "/dev/loop" ~ loopNumber.to!string;
432         associatedFileControl(filePath, loopDevice, flag);
433     }
434 
435     void associatedFileControl(string filePath, string device, int flag) {
436         int fd, loop;
437         fd = unix_fcntl.open(filePath.toStringz, unix_fcntl.O_RDWR);
438         enforce(-1 != fd, new FileException(filePath));
439         loop = unix_fcntl.open(device.toStringz, unix_fcntl.O_RDWR);
440         enforce(-1 != loop, new FileException(device));
441         int ret = ioctl(loop, flag, fd);
442         enforce(-1 != ret, new FileException("ioctl"));
443     }
444 }
445 
446 
447 alias loopctl = LoopDevControl;
448 
449 
450 /*******************************************************************************
451  * Returns group numeric identifier, given the known group name.
452  */
453 uint getGroupIdByName(string groupName) {
454     if (groupName.empty) {
455         return -1;
456     }
457     group* g = getgrnam(groupName.toStringz);
458     return (g is null) ? -1 : g.gr_gid;
459 }
460 deprecated alias getGroupIdWithName = getGroupIdByName;
461 
462 
463 private extern(C)
464 int getgroups(int size, gid_t* list) nothrow @nogc;
465 
466 private extern(C)
467 int getgrouplist(
468     const char* user, gid_t group, gid_t* groups, int* ngroups
469 ) nothrow @nogc;
470 
471 
472 /*******************************************************************************
473  * Returns all system group IDs (if UID isn't specified)
474  * or group IDs that the specified user is associated with.
475  */
476 gid_t[] getGroups(gid_t uid = uid_t.max) {
477     gid_t[] groups;
478     int ngroups;
479     if (uid == uid_t.max) {
480         ngroups = getgroups(0, null);
481         enforce(ngroups != 1, new SysException("getgroups"));
482         groups = new gid_t[ngroups];
483         int ret = getgroups(ngroups, groups.ptr);
484         enforce(ret != 1, new SysException("getgroups"));
485     } else {
486         passwd* p = getpwuid(uid);
487         const char* userNamePtr = (*p).pw_name;
488         int ret = getgrouplist(userNamePtr, (*p).pw_gid, null, &ngroups);
489         enforce(ret != 1, new SysException("getgrouplist"));
490         groups = new gid_t[ngroups];
491         ret = getgrouplist(userNamePtr, (*p).pw_gid, groups.ptr, &ngroups);
492         enforce(ret != 1, new SysException("getgrouplist"));
493     }
494     return groups;
495 }
496 
497 
498 /**
499  * Exit codes for programs.
500  * Codes and descriptions were copied from https://man.openbsd.org/sysexits.3
501  */
502 enum {
503     /**
504      * The command was used incorrectly, e.g., with the wrong number
505      * of arguments, a bad flag, bad syntax in a parameter, or whatever.
506      */
507     EX_USAGE = 64,
508 
509     /**
510      * The input data was incorrect in some way.
511      * This should only be used for user's data and not system files.
512      */
513     EX_DATAERR = 65,
514 
515     /**
516      * An input file (not a system file) did not exist or was not readable.
517      * This could also include errors like “No message” to a mailer
518      * (if it cared to catch it).
519      */
520     EX_NOINPUT = 66,
521 
522     /**
523      * The user specified did not exist.
524      * This might be used for mail addresses or remote logins.
525      */
526     EX_NOUSER = 67,
527 
528     /**
529      * The host specified did not exist.
530      * This is used in mail addresses or network requests.
531      */
532     EX_NOHOST = 68,
533 
534     /**
535      * A service is unavailable. This can occur if a support program or file
536      * does not exist. This can also be used as a catch-all message when
537      * something you wanted to do doesn't work, but you don't know why.
538      */
539     EX_UNAVAILABLE = 69,
540 
541     /**
542      * An internal software error has been detected. This should be limited
543      * to non-operating system related errors if possible.
544      */
545     EX_SOFTWARE = 70,
546 
547     /**
548      * An operating system error has been detected.
549      * This is intended to be used for such things as “cannot fork”,
550      * or “cannot create pipe”. It includes things like getuid(2) returning
551      * a user that does not exist in the passwd file.
552      */
553     EX_OSERR = 71,
554 
555     /**
556      * Some system file (e.g., /etc/passwd, /var/run/utmp) does not exist,
557      * cannot be opened, or has some sort of error (e.g., syntax error).
558      */
559     EX_OSFILE = 72,
560 
561     /**
562      * A (user specified) output file cannot be created.
563      */
564     EX_CANTCREAT = 73,
565 
566     /**
567      * An error occurred while doing I/O on some file.
568      */
569     EX_IOERR = 74,
570 
571     /**
572      * Temporary failure, indicating something that is not really an error.
573      * For example that a mailer could not create a connection, and the request
574      * should be reattempted later.
575      */
576     EX_TEMPFAIL = 75,
577 
578     /**
579      * The remote system returned something that was “not possible”
580      * during a protocol exchange.
581      */
582     EX_PROTOCOL= 76,
583 
584     /**
585      * You did not have sufficient permission to perform the operation.
586      * This is not intended for file system problems, which should use
587      * EX_NOINPUT or EX_CANTCREAT, but rather for higher level permissions.
588      */
589     EX_NOPERM = 77,
590 
591     /**
592      * Something was found in an unconfigured or misconfigured state.
593      */
594     EX_CONFIG = 78
595 }
596 
597 
598 /// Gets memory page size. See: `man getpagesize`
599 extern(C) int getpagesize() pure nothrow @nogc;
600 
601 
602 /**
603  * Enumeration of access modes for mappings.
604  */
605 enum MapMode {
606     none   = unix_mman.PROT_NONE,
607     rdOnly = unix_mman.PROT_READ,
608     wrOnly = unix_mman.PROT_WRITE,
609     exec   = unix_mman.PROT_EXEC,
610     rdWr   = unix_mman.PROT_READ | unix_mman.PROT_WRITE,
611 }
612 
613 
614 /*******************************************************************************
615  * Creates a new mapping for the specified file.
616  * Params:
617  *     f    = related file object 
618  *     size = size of allocatable memory in bytes
619  *     mode = access mode (read/write, by default)
620  */
621 void* createMapping(File f, size_t size, MapMode mode = MapMode.rdWr) {
622     auto flags = unix_mman.MAP_SHARED;
623     void* p = unix_mman.mmap(null, size, mode, flags, f.fileno, 0);
624     enforce(p != unix_mman.MAP_FAILED, new SysException("mmap"));
625     return p;
626 }
627 
628 
629 private void* anon_map_linux_style(size_t size) {
630     auto flags = linux_mman.MAP_ANONYMOUS | unix_mman.MAP_PRIVATE;
631     void* p = unix_mman.mmap(null, size, MapMode.rdWr, flags, -1, 0);
632     enforce(unix_mman.MAP_FAILED != p, new SysException("mmap"));
633     return p;
634 }
635 
636 
637 private void* anon_map_bsd_style(size_t size) {
638     enum zeroDevice = "/dev/zero";
639     int fd = unix_fcntl.open(zeroDevice, unix_fcntl.O_RDWR);
640     enforce(-1 != fd, new FileException(zeroDevice));
641     scope(exit) unix_unistd.close(fd);
642 
643     auto flags = unix_mman.MAP_PRIVATE;
644     void* p = unix_mman.mmap(null, size, MapMode.rdWr, flags, fd, 0);
645     enforce(unix_mman.MAP_FAILED != p, new SysException("mmap"));
646     return p;
647 }
648 
649 
650 /*******************************************************************************
651  * Allocates memory using anonymous mapping.
652  * New memory will contain zeros.
653  * Params:
654  *     size = size of allocatable memory in bytes
655  * Returns: pointer to allocatable memory area.
656  */
657 void* createAnonymousMapping(size_t size) {
658     version (linux) {
659         return anon_map_linux_style(size);
660     } else {
661         return anon_map_bsd_style(size);
662     }
663 }
664 alias anonMapping = createAnonymousMapping;
665 
666 
667 /*******************************************************************************
668  * Releases the mapping.
669  * Params:
670  *     p = pointer to mapping
671  *     size = size used during the creation of the mapping
672  */
673 void destroyMapping(T)(T* p, size_t size) {
674     int ret = unix_mman.munmap(cast(void*)(p), size);
675     enforce(ret == 0, new SysException("munmap"));
676 }
677 
678 
679 version (SPARC_Any) {
680     private enum SIGPWR_NO = 29;
681     private enum SIGWINCH_NO = 28;
682 } else version (MIPS_Any) {
683     private enum SIGPWR_NO = 19;
684     private enum SIGWINCH_NO = 20;
685 } else version (HPPA_Any) {
686     private enum SIGPWR_NO = 19;
687     private enum SIGWINCH_NO = 23;
688 } else {
689     private enum SIGPWR_NO = 30;
690     private enum SIGWINCH_NO = 28;
691 }
692 
693 /**
694  * UNIX signal enumeration.
695  */
696 enum Signal {
697     SIGHUP    = unix_sig.SIGHUP,     //< Hangup
698     SIGINT    = stdc_sig.SIGINT,     //< Interrupt from keyboard
699     SIGQUIT   = unix_sig.SIGQUIT,    //< Quit
700     SIGILL    = stdc_sig.SIGILL,     //< Illegal instruction
701     SIGTRAP   = unix_sig.SIGTRAP,    //< Trace/breakpoint trap
702     SIGABRT   = stdc_sig.SIGABRT,    //< Aborted
703     SIGIOT    = SIGABRT,             //< A synonym for SIGABRT
704     SIGBUS    = unix_sig.SIGBUS,     //< Bus error
705     SIGFPE    = stdc_sig.SIGFPE,     //< Floating point exception
706     SIGKILL   = unix_sig.SIGKILL,    //< Killed
707     SIGUSR1   = unix_sig.SIGUSR1,    //< User defined signal #1
708     SIGUSR2   = unix_sig.SIGUSR2,    //< User defined signal #2
709     SIGSEGV   = stdc_sig.SIGSEGV,    //< Segmentation fault
710     SIGPIPE   = unix_sig.SIGPIPE,    //< Broken pipe
711     SIGALRM   = unix_sig.SIGALRM,    //< Alarm clock
712     SIGTERM   = stdc_sig.SIGTERM,    //< Terminated
713     SIGCHLD   = unix_sig.SIGCHLD,    //< Child exited
714     SIGCONT   = unix_sig.SIGCONT,    //< Continued
715     SIGSTOP   = unix_sig.SIGSTOP,    //< Stop process
716     SIGTSTP   = unix_sig.SIGTSTP,    //< Stop typed at terminal
717     SIGTTIN   = unix_sig.SIGTTIN,    //< Terminal input for background process
718     SIGTTOU   = unix_sig.SIGTTOU,    //< Terminal output for background process
719     SIGURG    = unix_sig.SIGURG,     //< Urgent I/O condition on socket
720     SIGXCPU   = unix_sig.SIGXCPU,    //< CPU time limit exceeded
721     SIGXFSZ   = unix_sig.SIGXFSZ,    //< File size limit exceeded
722     SIGVTALRM = unix_sig.SIGVTALRM,  //< Virtual timer expired
723     SIGPROF   = unix_sig.SIGPROF,    //< Profiling timer expired
724     SIGWINCH  = SIGWINCH_NO,         //< Window changed
725     SIGPOLL   = unix_sig.SIGPOLL,    //< Pollable event, synonym for SIGIO
726     SIGIO     = SIGPOLL,             //< I/O possible
727     SIGPWR    = SIGPWR_NO,           //< Power failure
728     SIGSYS    = unix_sig.SIGSYS,     //< Bad system call
729     SIGUNUSED = SIGSYS               //< A synonym for SIGSYS
730 }
731 
732 alias c_sigfn_t = extern(C) void function(int);
733 alias d_sigfn_t = extern(D) void function(Signal);
734 alias d_sigfn_int_t = extern(D) void function(int);
735 
736 /*******************************************************************************
737  * The function sets the handler for the signal number.
738  * Params:
739  *     sig = signal number of Signal or int-like type
740  *     handler = signal handler of c_sigfn_t or d_sigfn_t type 
741  */
742 void addSignalHandler(S, H)(S sig, H handler)
743 if (
744     (is(S == Signal) || is(S : int)) &&
745     (is(H == c_sigfn_t) || is(H == d_sigfn_t) || is(H == d_sigfn_int_t))
746 ) {
747     unix_sig.sigset_t sigset;
748     unix_sig.sigemptyset(&sigset);
749 
750     unix_sig.sigaction_t sigact;
751     sigact.sa_handler = cast(c_sigfn_t)handler;
752     sigact.sa_mask = sigset;
753     sigact.sa_flags = unix_sig.SA_RESTART;
754 
755     if (-1 == unix_sig.sigaction(sig, &sigact, null)) {
756         string sigName = (cast(Signal)sig).to!string;
757         string msg = format!"Unable to add signal %d (%s)"(sig, sigName);
758         throw new SysException(msg);
759     }
760 }
761 
762 
763 extern(C) private char* strsignal(int signum) pure nothrow @nogc;
764 
765 
766 /*******************************************************************************
767  * Gets UNIX signal string view.
768  *
769  * Params:
770  *     signal = explored signal value
771  * Returns:
772  *     string interpretation of the signal
773  *
774  */
775 string getSignalDescription(Signal signal) {
776     return getSignalDescription(cast(int)signal);
777 }
778 // ditto
779 string getSignalDescription(int signum) {
780     return strsignal(signum).fromStringz.idup;
781 }
782 
783 
784 /// Exception for functions of this module
785 class SysException : Exception { mixin RealizeErrnoException; }
786 
787 
788 // -------------------------------------------------------------------------- //
789 // -------------------------- Deprecated things ----------------------------- //
790 // -------------------------------------------------------------------------- //
791 
792 deprecated void freeAnonymousMapping(void* p, size_t size) {
793     int ret = unix_mman.munmap(p, size);
794     enforce(ret == 0, new SysException("munmap"));
795 }
796 deprecated alias free_anonymous_mapping = freeAnonymousMapping;
797 deprecated alias create_anonymous_mapping = createAnonymousMapping;
798 
799 deprecated void setupSignalHandler(int sig, c_sigfn_t handler) {
800     addSignalHandler(sig, handler);
801 }
802 deprecated void setupSignalHandler(int sig, void function(int) handler) {
803     addSignalHandler(sig, handler);
804 }
805 deprecated void setupSignalHandler(Signal sig, void function(Signal) handler) {
806     addSignalHandler(sig, handler);
807 }