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 }