diff options
| author | Ron Yorston <rmy@pobox.com> | 2017-05-29 14:20:10 +0100 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2017-05-29 14:34:28 +0100 |
| commit | da4f331955bed8afda670afcd58d524a04a0faa9 (patch) | |
| tree | f6a3879aefdd714240f8c022375f687b512d2238 /miscutils | |
| parent | 74163a535fd21f5fcca4c052d2e7c192d3e264fa (diff) | |
| parent | 6683d1cbb44859f549f87f882545b84b9369585c (diff) | |
| download | busybox-w32-da4f331955bed8afda670afcd58d524a04a0faa9.tar.gz busybox-w32-da4f331955bed8afda670afcd58d524a04a0faa9.tar.bz2 busybox-w32-da4f331955bed8afda670afcd58d524a04a0faa9.zip | |
Merge branch 'busybox' into merge
Diffstat (limited to 'miscutils')
| -rw-r--r-- | miscutils/chrt.c | 149 | ||||
| -rw-r--r-- | miscutils/eject.c | 152 | ||||
| -rw-r--r-- | miscutils/inotifyd.c | 2 | ||||
| -rw-r--r-- | miscutils/ionice.c | 115 | ||||
| -rw-r--r-- | miscutils/last.c | 166 | ||||
| -rw-r--r-- | miscutils/last_fancy.c | 300 | ||||
| -rw-r--r-- | miscutils/less.c | 2 | ||||
| -rw-r--r-- | miscutils/lsscsi.c | 123 | ||||
| -rw-r--r-- | miscutils/mountpoint.c | 105 | ||||
| -rw-r--r-- | miscutils/partprobe.c | 56 | ||||
| -rw-r--r-- | miscutils/setsid.c | 82 | ||||
| -rw-r--r-- | miscutils/taskset.c | 221 | ||||
| -rw-r--r-- | miscutils/time.c | 49 | ||||
| -rw-r--r-- | miscutils/timeout.c | 127 | ||||
| -rw-r--r-- | miscutils/wall.c | 63 |
15 files changed, 218 insertions, 1494 deletions
diff --git a/miscutils/chrt.c b/miscutils/chrt.c deleted file mode 100644 index 1604a6890..000000000 --- a/miscutils/chrt.c +++ /dev/null | |||
| @@ -1,149 +0,0 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * chrt - manipulate real-time attributes of a process | ||
| 4 | * Copyright (c) 2006-2007 Bernhard Reutner-Fischer | ||
| 5 | * | ||
| 6 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
| 7 | */ | ||
| 8 | //config:config CHRT | ||
| 9 | //config: bool "chrt" | ||
| 10 | //config: default y | ||
| 11 | //config: help | ||
| 12 | //config: manipulate real-time attributes of a process. | ||
| 13 | //config: This requires sched_{g,s}etparam support in your libc. | ||
| 14 | |||
| 15 | //applet:IF_CHRT(APPLET(chrt, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
| 16 | |||
| 17 | //kbuild:lib-$(CONFIG_CHRT) += chrt.o | ||
| 18 | |||
| 19 | //usage:#define chrt_trivial_usage | ||
| 20 | //usage: "[-prfom] [PRIO] [PID | PROG ARGS]" | ||
| 21 | //usage:#define chrt_full_usage "\n\n" | ||
| 22 | //usage: "Change scheduling priority and class for a process\n" | ||
| 23 | //usage: "\n -p Operate on PID" | ||
| 24 | //usage: "\n -r Set SCHED_RR class" | ||
| 25 | //usage: "\n -f Set SCHED_FIFO class" | ||
| 26 | //usage: "\n -o Set SCHED_OTHER class" | ||
| 27 | //usage: "\n -m Show min/max priorities" | ||
| 28 | //usage: | ||
| 29 | //usage:#define chrt_example_usage | ||
| 30 | //usage: "$ chrt -r 4 sleep 900; x=$!\n" | ||
| 31 | //usage: "$ chrt -f -p 3 $x\n" | ||
| 32 | //usage: "You need CAP_SYS_NICE privileges to set scheduling attributes of a process" | ||
| 33 | |||
| 34 | #include <sched.h> | ||
| 35 | #include "libbb.h" | ||
| 36 | |||
| 37 | static const struct { | ||
| 38 | int policy; | ||
| 39 | char name[sizeof("SCHED_OTHER")]; | ||
| 40 | } policies[] = { | ||
| 41 | {SCHED_OTHER, "SCHED_OTHER"}, | ||
| 42 | {SCHED_FIFO, "SCHED_FIFO"}, | ||
| 43 | {SCHED_RR, "SCHED_RR"} | ||
| 44 | }; | ||
| 45 | |||
| 46 | //TODO: add | ||
| 47 | // -b, SCHED_BATCH | ||
| 48 | // -i, SCHED_IDLE | ||
| 49 | |||
| 50 | static void show_min_max(int pol) | ||
| 51 | { | ||
| 52 | const char *fmt = "%s min/max priority\t: %u/%u\n"; | ||
| 53 | int max, min; | ||
| 54 | |||
| 55 | max = sched_get_priority_max(pol); | ||
| 56 | min = sched_get_priority_min(pol); | ||
| 57 | if ((max|min) < 0) | ||
| 58 | fmt = "%s not supported\n"; | ||
| 59 | printf(fmt, policies[pol].name, min, max); | ||
| 60 | } | ||
| 61 | |||
| 62 | #define OPT_m (1<<0) | ||
| 63 | #define OPT_p (1<<1) | ||
| 64 | #define OPT_r (1<<2) | ||
| 65 | #define OPT_f (1<<3) | ||
| 66 | #define OPT_o (1<<4) | ||
| 67 | |||
| 68 | int chrt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 69 | int chrt_main(int argc UNUSED_PARAM, char **argv) | ||
| 70 | { | ||
| 71 | pid_t pid = 0; | ||
| 72 | unsigned opt; | ||
| 73 | struct sched_param sp; | ||
| 74 | char *pid_str; | ||
| 75 | char *priority = priority; /* for compiler */ | ||
| 76 | const char *current_new; | ||
| 77 | int policy = SCHED_RR; | ||
| 78 | |||
| 79 | /* only one policy accepted */ | ||
| 80 | opt_complementary = "r--fo:f--ro:o--rf"; | ||
| 81 | opt = getopt32(argv, "+mprfo"); | ||
| 82 | if (opt & OPT_m) { /* print min/max and exit */ | ||
| 83 | show_min_max(SCHED_FIFO); | ||
| 84 | show_min_max(SCHED_RR); | ||
| 85 | show_min_max(SCHED_OTHER); | ||
| 86 | fflush_stdout_and_exit(EXIT_SUCCESS); | ||
| 87 | } | ||
| 88 | if (opt & OPT_r) | ||
| 89 | policy = SCHED_RR; | ||
| 90 | if (opt & OPT_f) | ||
| 91 | policy = SCHED_FIFO; | ||
| 92 | if (opt & OPT_o) | ||
| 93 | policy = SCHED_OTHER; | ||
| 94 | |||
| 95 | argv += optind; | ||
| 96 | if (!argv[0]) | ||
| 97 | bb_show_usage(); | ||
| 98 | if (opt & OPT_p) { | ||
| 99 | pid_str = *argv++; | ||
| 100 | if (*argv) { /* "-p <priority> <pid> [...]" */ | ||
| 101 | priority = pid_str; | ||
| 102 | pid_str = *argv; | ||
| 103 | } | ||
| 104 | /* else "-p <pid>", and *argv == NULL */ | ||
| 105 | pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1); | ||
| 106 | } else { | ||
| 107 | priority = *argv++; | ||
| 108 | if (!*argv) | ||
| 109 | bb_show_usage(); | ||
| 110 | } | ||
| 111 | |||
| 112 | current_new = "current\0new"; | ||
| 113 | if (opt & OPT_p) { | ||
| 114 | int pol; | ||
| 115 | print_rt_info: | ||
| 116 | pol = sched_getscheduler(pid); | ||
| 117 | if (pol < 0) | ||
| 118 | bb_perror_msg_and_die("can't %cet pid %d's policy", 'g', (int)pid); | ||
| 119 | printf("pid %d's %s scheduling policy: %s\n", | ||
| 120 | pid, current_new, policies[pol].name); | ||
| 121 | if (sched_getparam(pid, &sp)) | ||
| 122 | bb_perror_msg_and_die("can't get pid %d's attributes", (int)pid); | ||
| 123 | printf("pid %d's %s scheduling priority: %d\n", | ||
| 124 | (int)pid, current_new, sp.sched_priority); | ||
| 125 | if (!*argv) { | ||
| 126 | /* Either it was just "-p <pid>", | ||
| 127 | * or it was "-p <priority> <pid>" and we came here | ||
| 128 | * for the second time (see goto below) */ | ||
| 129 | return EXIT_SUCCESS; | ||
| 130 | } | ||
| 131 | *argv = NULL; | ||
| 132 | current_new += 8; | ||
| 133 | } | ||
| 134 | |||
| 135 | /* from the manpage of sched_getscheduler: | ||
| 136 | [...] sched_priority can have a value in the range 0 to 99. | ||
| 137 | [...] SCHED_OTHER or SCHED_BATCH must be assigned static priority 0. | ||
| 138 | [...] SCHED_FIFO or SCHED_RR can have static priority in 1..99 range. | ||
| 139 | */ | ||
| 140 | sp.sched_priority = xstrtou_range(priority, 0, policy != SCHED_OTHER ? 1 : 0, 99); | ||
| 141 | |||
| 142 | if (sched_setscheduler(pid, policy, &sp) < 0) | ||
| 143 | bb_perror_msg_and_die("can't %cet pid %d's policy", 's', (int)pid); | ||
| 144 | |||
| 145 | if (!argv[0]) /* "-p <priority> <pid> [...]" */ | ||
| 146 | goto print_rt_info; | ||
| 147 | |||
| 148 | BB_EXECVP_or_die(argv); | ||
| 149 | } | ||
diff --git a/miscutils/eject.c b/miscutils/eject.c deleted file mode 100644 index 667932f6c..000000000 --- a/miscutils/eject.c +++ /dev/null | |||
| @@ -1,152 +0,0 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * eject implementation for busybox | ||
| 4 | * | ||
| 5 | * Copyright (C) 2004 Peter Willis <psyphreak@phreaker.net> | ||
| 6 | * Copyright (C) 2005 Tito Ragusa <farmatito@tiscali.it> | ||
| 7 | * | ||
| 8 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
| 9 | */ | ||
| 10 | |||
| 11 | /* | ||
| 12 | * This is a simple hack of eject based on something Erik posted in #uclibc. | ||
| 13 | * Most of the dirty work blatantly ripped off from cat.c =) | ||
| 14 | */ | ||
| 15 | //config:config EJECT | ||
| 16 | //config: bool "eject" | ||
| 17 | //config: default y | ||
| 18 | //config: select PLATFORM_LINUX | ||
| 19 | //config: help | ||
| 20 | //config: Used to eject cdroms. (defaults to /dev/cdrom) | ||
| 21 | //config: | ||
| 22 | //config:config FEATURE_EJECT_SCSI | ||
| 23 | //config: bool "SCSI support" | ||
| 24 | //config: default y | ||
| 25 | //config: depends on EJECT | ||
| 26 | //config: help | ||
| 27 | //config: Add the -s option to eject, this allows to eject SCSI-Devices and | ||
| 28 | //config: usb-storage devices. | ||
| 29 | |||
| 30 | //applet:IF_EJECT(APPLET(eject, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
| 31 | |||
| 32 | //kbuild:lib-$(CONFIG_EJECT) += eject.o | ||
| 33 | |||
| 34 | //usage:#define eject_trivial_usage | ||
| 35 | //usage: "[-t] [-T] [DEVICE]" | ||
| 36 | //usage:#define eject_full_usage "\n\n" | ||
| 37 | //usage: "Eject DEVICE or default /dev/cdrom\n" | ||
| 38 | //usage: IF_FEATURE_EJECT_SCSI( | ||
| 39 | //usage: "\n -s SCSI device" | ||
| 40 | //usage: ) | ||
| 41 | //usage: "\n -t Close tray" | ||
| 42 | //usage: "\n -T Open/close tray (toggle)" | ||
| 43 | |||
| 44 | #include <sys/mount.h> | ||
| 45 | #include "libbb.h" | ||
| 46 | #if ENABLE_FEATURE_EJECT_SCSI | ||
| 47 | /* Must be after libbb.h: they need size_t */ | ||
| 48 | # include "fix_u32.h" | ||
| 49 | # include <scsi/sg.h> | ||
| 50 | # include <scsi/scsi.h> | ||
| 51 | #endif | ||
| 52 | |||
| 53 | #define dev_fd 3 | ||
| 54 | |||
| 55 | /* Code taken from the original eject (http://eject.sourceforge.net/), | ||
| 56 | * refactored it a bit for busybox (ne-bb@nicoerfurth.de) */ | ||
| 57 | |||
| 58 | #if ENABLE_FEATURE_EJECT_SCSI | ||
| 59 | static void eject_scsi(const char *dev) | ||
| 60 | { | ||
| 61 | static const char sg_commands[3][6] ALIGN1 = { | ||
| 62 | { ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0 }, | ||
| 63 | { START_STOP, 0, 0, 0, 1, 0 }, | ||
| 64 | { START_STOP, 0, 0, 0, 2, 0 } | ||
| 65 | }; | ||
| 66 | |||
| 67 | unsigned i; | ||
| 68 | unsigned char sense_buffer[32]; | ||
| 69 | unsigned char inqBuff[2]; | ||
| 70 | sg_io_hdr_t io_hdr; | ||
| 71 | |||
| 72 | if ((ioctl(dev_fd, SG_GET_VERSION_NUM, &i) < 0) || (i < 30000)) | ||
| 73 | bb_error_msg_and_die("not a sg device or old sg driver"); | ||
| 74 | |||
| 75 | memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); | ||
| 76 | io_hdr.interface_id = 'S'; | ||
| 77 | io_hdr.cmd_len = 6; | ||
| 78 | io_hdr.mx_sb_len = sizeof(sense_buffer); | ||
| 79 | io_hdr.dxfer_direction = SG_DXFER_NONE; | ||
| 80 | /* io_hdr.dxfer_len = 0; */ | ||
| 81 | io_hdr.dxferp = inqBuff; | ||
| 82 | io_hdr.sbp = sense_buffer; | ||
| 83 | io_hdr.timeout = 2000; | ||
| 84 | |||
| 85 | for (i = 0; i < 3; i++) { | ||
| 86 | io_hdr.cmdp = (void *)sg_commands[i]; | ||
| 87 | ioctl_or_perror_and_die(dev_fd, SG_IO, (void *)&io_hdr, "%s", dev); | ||
| 88 | } | ||
| 89 | |||
| 90 | /* force kernel to reread partition table when new disc is inserted */ | ||
| 91 | ioctl(dev_fd, BLKRRPART); | ||
| 92 | } | ||
| 93 | #else | ||
| 94 | # define eject_scsi(dev) ((void)0) | ||
| 95 | #endif | ||
| 96 | |||
| 97 | /* various defines swiped from linux/cdrom.h */ | ||
| 98 | #define CDROMCLOSETRAY 0x5319 /* pendant of CDROMEJECT */ | ||
| 99 | #define CDROMEJECT 0x5309 /* Ejects the cdrom media */ | ||
| 100 | #define CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */ | ||
| 101 | /* drive status possibilities returned by CDROM_DRIVE_STATUS ioctl */ | ||
| 102 | #define CDS_TRAY_OPEN 2 | ||
| 103 | |||
| 104 | #define FLAG_CLOSE 1 | ||
| 105 | #define FLAG_SMART 2 | ||
| 106 | #define FLAG_SCSI 4 | ||
| 107 | |||
| 108 | static void eject_cdrom(unsigned flags, const char *dev) | ||
| 109 | { | ||
| 110 | int cmd = CDROMEJECT; | ||
| 111 | |||
| 112 | if (flags & FLAG_CLOSE | ||
| 113 | || ((flags & FLAG_SMART) && ioctl(dev_fd, CDROM_DRIVE_STATUS) == CDS_TRAY_OPEN) | ||
| 114 | ) { | ||
| 115 | cmd = CDROMCLOSETRAY; | ||
| 116 | } | ||
| 117 | |||
| 118 | ioctl_or_perror_and_die(dev_fd, cmd, NULL, "%s", dev); | ||
| 119 | } | ||
| 120 | |||
| 121 | int eject_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 122 | int eject_main(int argc UNUSED_PARAM, char **argv) | ||
| 123 | { | ||
| 124 | unsigned flags; | ||
| 125 | const char *device; | ||
| 126 | |||
| 127 | opt_complementary = "?1:t--T:T--t"; | ||
| 128 | flags = getopt32(argv, "tT" IF_FEATURE_EJECT_SCSI("s")); | ||
| 129 | device = argv[optind] ? argv[optind] : "/dev/cdrom"; | ||
| 130 | |||
| 131 | /* We used to do "umount <device>" here, but it was buggy | ||
| 132 | if something was mounted OVER cdrom and | ||
| 133 | if cdrom is mounted many times. | ||
| 134 | |||
| 135 | This works equally well (or better): | ||
| 136 | #!/bin/sh | ||
| 137 | umount /dev/cdrom | ||
| 138 | eject /dev/cdrom | ||
| 139 | */ | ||
| 140 | |||
| 141 | xmove_fd(xopen_nonblocking(device), dev_fd); | ||
| 142 | |||
| 143 | if (ENABLE_FEATURE_EJECT_SCSI && (flags & FLAG_SCSI)) | ||
| 144 | eject_scsi(device); | ||
| 145 | else | ||
| 146 | eject_cdrom(flags, device); | ||
| 147 | |||
| 148 | if (ENABLE_FEATURE_CLEAN_UP) | ||
| 149 | close(dev_fd); | ||
| 150 | |||
| 151 | return EXIT_SUCCESS; | ||
| 152 | } | ||
diff --git a/miscutils/inotifyd.c b/miscutils/inotifyd.c index 601df6465..db8ddce92 100644 --- a/miscutils/inotifyd.c +++ b/miscutils/inotifyd.c | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | * Use as follows: | 12 | * Use as follows: |
| 13 | * # inotifyd /user/space/agent dir/or/file/being/watched[:mask] ... | 13 | * # inotifyd /user/space/agent dir/or/file/being/watched[:mask] ... |
| 14 | * | 14 | * |
| 15 | * When a filesystem event matching the specified mask is occured on specified file (or directory) | 15 | * When a filesystem event matching the specified mask is occurred on specified file (or directory) |
| 16 | * a userspace agent is spawned and given the following parameters: | 16 | * a userspace agent is spawned and given the following parameters: |
| 17 | * $1. actual event(s) | 17 | * $1. actual event(s) |
| 18 | * $2. file (or directory) name | 18 | * $2. file (or directory) name |
diff --git a/miscutils/ionice.c b/miscutils/ionice.c deleted file mode 100644 index c54b3a6e1..000000000 --- a/miscutils/ionice.c +++ /dev/null | |||
| @@ -1,115 +0,0 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * ionice implementation for busybox based on linux-utils-ng 2.14 | ||
| 4 | * | ||
| 5 | * Copyright (C) 2008 by <u173034@informatik.uni-oldenburg.de> | ||
| 6 | * | ||
| 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
| 8 | */ | ||
| 9 | //config:config IONICE | ||
| 10 | //config: bool "ionice" | ||
| 11 | //config: default y | ||
| 12 | //config: select PLATFORM_LINUX | ||
| 13 | //config: help | ||
| 14 | //config: Set/set program io scheduling class and priority | ||
| 15 | //config: Requires kernel >= 2.6.13 | ||
| 16 | |||
| 17 | //applet:IF_IONICE(APPLET(ionice, BB_DIR_BIN, BB_SUID_DROP)) | ||
| 18 | |||
| 19 | //kbuild:lib-$(CONFIG_IONICE) += ionice.o | ||
| 20 | |||
| 21 | //usage:#define ionice_trivial_usage | ||
| 22 | //usage: "[-c 1-3] [-n 0-7] [-p PID] [PROG]" | ||
| 23 | //usage:#define ionice_full_usage "\n\n" | ||
| 24 | //usage: "Change I/O priority and class\n" | ||
| 25 | //usage: "\n -c Class. 1:realtime 2:best-effort 3:idle" | ||
| 26 | //usage: "\n -n Priority" | ||
| 27 | |||
| 28 | #include <sys/syscall.h> | ||
| 29 | #include <asm/unistd.h> | ||
| 30 | #include "libbb.h" | ||
| 31 | |||
| 32 | static int ioprio_set(int which, int who, int ioprio) | ||
| 33 | { | ||
| 34 | return syscall(SYS_ioprio_set, which, who, ioprio); | ||
| 35 | } | ||
| 36 | |||
| 37 | static int ioprio_get(int which, int who) | ||
| 38 | { | ||
| 39 | return syscall(SYS_ioprio_get, which, who); | ||
| 40 | } | ||
| 41 | |||
| 42 | enum { | ||
| 43 | IOPRIO_WHO_PROCESS = 1, | ||
| 44 | IOPRIO_WHO_PGRP, | ||
| 45 | IOPRIO_WHO_USER | ||
| 46 | }; | ||
| 47 | |||
| 48 | enum { | ||
| 49 | IOPRIO_CLASS_NONE, | ||
| 50 | IOPRIO_CLASS_RT, | ||
| 51 | IOPRIO_CLASS_BE, | ||
| 52 | IOPRIO_CLASS_IDLE | ||
| 53 | }; | ||
| 54 | |||
| 55 | static const char to_prio[] ALIGN1 = "none\0realtime\0best-effort\0idle"; | ||
| 56 | |||
| 57 | #define IOPRIO_CLASS_SHIFT 13 | ||
| 58 | |||
| 59 | int ionice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 60 | int ionice_main(int argc UNUSED_PARAM, char **argv) | ||
| 61 | { | ||
| 62 | /* Defaults */ | ||
| 63 | int ioclass = 0; | ||
| 64 | int pri = 0; | ||
| 65 | int pid = 0; /* affect own porcess */ | ||
| 66 | int opt; | ||
| 67 | enum { | ||
| 68 | OPT_n = 1, | ||
| 69 | OPT_c = 2, | ||
| 70 | OPT_p = 4, | ||
| 71 | }; | ||
| 72 | |||
| 73 | /* Numeric params */ | ||
| 74 | /* '+': stop at first non-option */ | ||
| 75 | opt = getopt32(argv, "+n:+c:+p:+", &pri, &ioclass, &pid); | ||
| 76 | argv += optind; | ||
| 77 | |||
| 78 | if (opt & OPT_c) { | ||
| 79 | if (ioclass > 3) | ||
| 80 | bb_error_msg_and_die("bad class %d", ioclass); | ||
| 81 | // Do we need this (compat?)? | ||
| 82 | // if (ioclass == IOPRIO_CLASS_NONE) | ||
| 83 | // ioclass = IOPRIO_CLASS_BE; | ||
| 84 | // if (ioclass == IOPRIO_CLASS_IDLE) { | ||
| 85 | // //if (opt & OPT_n) | ||
| 86 | // // bb_error_msg("ignoring priority for idle class"); | ||
| 87 | // pri = 7; | ||
| 88 | // } | ||
| 89 | } | ||
| 90 | |||
| 91 | if (!(opt & (OPT_n|OPT_c))) { | ||
| 92 | if (!(opt & OPT_p) && *argv) | ||
| 93 | pid = xatoi_positive(*argv); | ||
| 94 | |||
| 95 | pri = ioprio_get(IOPRIO_WHO_PROCESS, pid); | ||
| 96 | if (pri == -1) | ||
| 97 | bb_perror_msg_and_die("ioprio_%cet", 'g'); | ||
| 98 | |||
| 99 | ioclass = (pri >> IOPRIO_CLASS_SHIFT) & 0x3; | ||
| 100 | pri &= 0xff; | ||
| 101 | printf((ioclass == IOPRIO_CLASS_IDLE) ? "%s\n" : "%s: prio %d\n", | ||
| 102 | nth_string(to_prio, ioclass), pri); | ||
| 103 | } else { | ||
| 104 | //printf("pri=%d class=%d val=%x\n", | ||
| 105 | //pri, ioclass, pri | (ioclass << IOPRIO_CLASS_SHIFT)); | ||
| 106 | pri |= (ioclass << IOPRIO_CLASS_SHIFT); | ||
| 107 | if (ioprio_set(IOPRIO_WHO_PROCESS, pid, pri) == -1) | ||
| 108 | bb_perror_msg_and_die("ioprio_%cet", 's'); | ||
| 109 | if (argv[0]) { | ||
| 110 | BB_EXECVP_or_die(argv); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | return EXIT_SUCCESS; | ||
| 115 | } | ||
diff --git a/miscutils/last.c b/miscutils/last.c deleted file mode 100644 index b3f125c3f..000000000 --- a/miscutils/last.c +++ /dev/null | |||
| @@ -1,166 +0,0 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * last implementation for busybox | ||
| 4 | * | ||
| 5 | * Copyright (C) 2003-2004 by Erik Andersen <andersen@codepoet.org> | ||
| 6 | * | ||
| 7 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
| 8 | */ | ||
| 9 | //config:config LAST | ||
| 10 | //config: bool "last" | ||
| 11 | //config: default y | ||
| 12 | //config: depends on FEATURE_WTMP | ||
| 13 | //config: help | ||
| 14 | //config: 'last' displays a list of the last users that logged into the system. | ||
| 15 | //config: | ||
| 16 | //config:config FEATURE_LAST_FANCY | ||
| 17 | //config: bool "Output extra information" | ||
| 18 | //config: default y | ||
| 19 | //config: depends on LAST | ||
| 20 | //config: help | ||
| 21 | //config: 'last' displays detailed information about the last users that | ||
| 22 | //config: logged into the system (mimics sysvinit last). +900 bytes. | ||
| 23 | |||
| 24 | //applet:IF_LAST(APPLET(last, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
| 25 | |||
| 26 | //kbuild:ifeq ($(CONFIG_FEATURE_LAST_FANCY),y) | ||
| 27 | //kbuild:lib-$(CONFIG_FEATURE_LAST_FANCY) += last_fancy.o | ||
| 28 | //kbuild:else | ||
| 29 | //kbuild:lib-$(CONFIG_LAST) += last.o | ||
| 30 | //kbuild:endif | ||
| 31 | |||
| 32 | //usage:#define last_trivial_usage | ||
| 33 | //usage: ""IF_FEATURE_LAST_FANCY("[-HW] [-f FILE]") | ||
| 34 | //usage:#define last_full_usage "\n\n" | ||
| 35 | //usage: "Show listing of the last users that logged into the system" | ||
| 36 | //usage: IF_FEATURE_LAST_FANCY( "\n" | ||
| 37 | /* //usage: "\n -H Show header line" */ | ||
| 38 | //usage: "\n -W Display with no host column truncation" | ||
| 39 | //usage: "\n -f FILE Read from FILE instead of /var/log/wtmp" | ||
| 40 | //usage: ) | ||
| 41 | |||
| 42 | #include "libbb.h" | ||
| 43 | |||
| 44 | /* NB: ut_name and ut_user are the same field, use only one name (ut_user) | ||
| 45 | * to reduce confusion */ | ||
| 46 | |||
| 47 | #ifndef SHUTDOWN_TIME | ||
| 48 | # define SHUTDOWN_TIME 254 | ||
| 49 | #endif | ||
| 50 | |||
| 51 | /* Grr... utmp char[] members do not have to be nul-terminated. | ||
| 52 | * Do what we can while still keeping this reasonably small. | ||
| 53 | * Note: We are assuming the ut_id[] size is fixed at 4. */ | ||
| 54 | |||
| 55 | #if defined UT_LINESIZE \ | ||
| 56 | && ((UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256)) | ||
| 57 | #error struct utmpx member char[] size(s) have changed! | ||
| 58 | #elif defined __UT_LINESIZE \ | ||
| 59 | && ((__UT_LINESIZE != 32) || (__UT_NAMESIZE != 32) || (__UT_HOSTSIZE != 256)) | ||
| 60 | /* __UT_NAMESIZE was checked with 64 above, but glibc-2.11 definitely uses 32! */ | ||
| 61 | #error struct utmpx member char[] size(s) have changed! | ||
| 62 | #endif | ||
| 63 | |||
| 64 | #if EMPTY != 0 || RUN_LVL != 1 || BOOT_TIME != 2 || NEW_TIME != 3 || \ | ||
| 65 | OLD_TIME != 4 | ||
| 66 | #error Values for the ut_type field of struct utmpx changed | ||
| 67 | #endif | ||
| 68 | |||
| 69 | int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 70 | int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | ||
| 71 | { | ||
| 72 | struct utmpx ut; | ||
| 73 | int n, file = STDIN_FILENO; | ||
| 74 | time_t t_tmp; | ||
| 75 | off_t pos; | ||
| 76 | static const char _ut_usr[] ALIGN1 = | ||
| 77 | "runlevel\0" "reboot\0" "shutdown\0"; | ||
| 78 | static const char _ut_lin[] ALIGN1 = | ||
| 79 | "~\0" "{\0" "|\0" /* "LOGIN\0" "date\0" */; | ||
| 80 | enum { | ||
| 81 | TYPE_RUN_LVL = RUN_LVL, /* 1 */ | ||
| 82 | TYPE_BOOT_TIME = BOOT_TIME, /* 2 */ | ||
| 83 | TYPE_SHUTDOWN_TIME = SHUTDOWN_TIME | ||
| 84 | }; | ||
| 85 | enum { | ||
| 86 | _TILDE = EMPTY, /* 0 */ | ||
| 87 | TYPE_NEW_TIME, /* NEW_TIME, 3 */ | ||
| 88 | TYPE_OLD_TIME /* OLD_TIME, 4 */ | ||
| 89 | }; | ||
| 90 | |||
| 91 | if (argv[1]) { | ||
| 92 | bb_show_usage(); | ||
| 93 | } | ||
| 94 | file = xopen(bb_path_wtmp_file, O_RDONLY); | ||
| 95 | |||
| 96 | printf("%-10s %-14s %-18s %-12.12s %s\n", | ||
| 97 | "USER", "TTY", "HOST", "LOGIN", "TIME"); | ||
| 98 | /* yikes. We reverse over the file and that is a not too elegant way */ | ||
| 99 | pos = xlseek(file, 0, SEEK_END); | ||
| 100 | pos = lseek(file, pos - sizeof(ut), SEEK_SET); | ||
| 101 | while ((n = full_read(file, &ut, sizeof(ut))) > 0) { | ||
| 102 | if (n != sizeof(ut)) { | ||
| 103 | bb_perror_msg_and_die("short read"); | ||
| 104 | } | ||
| 105 | n = index_in_strings(_ut_lin, ut.ut_line); | ||
| 106 | if (n == _TILDE) { /* '~' */ | ||
| 107 | #if 1 | ||
| 108 | /* do we really need to be cautious here? */ | ||
| 109 | n = index_in_strings(_ut_usr, ut.ut_user); | ||
| 110 | if (++n > 0) | ||
| 111 | ut.ut_type = n != 3 ? n : SHUTDOWN_TIME; | ||
| 112 | #else | ||
| 113 | if (is_prefixed_with(ut.ut_user, "shutdown")) | ||
| 114 | ut.ut_type = SHUTDOWN_TIME; | ||
| 115 | else if (is_prefixed_with(ut.ut_user, "reboot")) | ||
| 116 | ut.ut_type = BOOT_TIME; | ||
| 117 | else if (is_prefixed_with(ut.ut_user, "runlevel")) | ||
| 118 | ut.ut_type = RUN_LVL; | ||
| 119 | #endif | ||
| 120 | } else { | ||
| 121 | if (ut.ut_user[0] == '\0' || strcmp(ut.ut_user, "LOGIN") == 0) { | ||
| 122 | /* Don't bother. This means we can't find how long | ||
| 123 | * someone was logged in for. Oh well. */ | ||
| 124 | goto next; | ||
| 125 | } | ||
| 126 | if (ut.ut_type != DEAD_PROCESS | ||
| 127 | && ut.ut_user[0] | ||
| 128 | && ut.ut_line[0] | ||
| 129 | ) { | ||
| 130 | ut.ut_type = USER_PROCESS; | ||
| 131 | } | ||
| 132 | if (strcmp(ut.ut_user, "date") == 0) { | ||
| 133 | if (n == TYPE_OLD_TIME) { /* '|' */ | ||
| 134 | ut.ut_type = OLD_TIME; | ||
| 135 | } | ||
| 136 | if (n == TYPE_NEW_TIME) { /* '{' */ | ||
| 137 | ut.ut_type = NEW_TIME; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | if (ut.ut_type != USER_PROCESS) { | ||
| 143 | switch (ut.ut_type) { | ||
| 144 | case OLD_TIME: | ||
| 145 | case NEW_TIME: | ||
| 146 | case RUN_LVL: | ||
| 147 | case SHUTDOWN_TIME: | ||
| 148 | goto next; | ||
| 149 | case BOOT_TIME: | ||
| 150 | strcpy(ut.ut_line, "system boot"); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | /* manpages say ut_tv.tv_sec *is* time_t, | ||
| 154 | * but some systems have it wrong */ | ||
| 155 | t_tmp = (time_t)ut.ut_tv.tv_sec; | ||
| 156 | printf("%-10s %-14s %-18s %-12.12s\n", | ||
| 157 | ut.ut_user, ut.ut_line, ut.ut_host, ctime(&t_tmp) + 4); | ||
| 158 | next: | ||
| 159 | pos -= sizeof(ut); | ||
| 160 | if (pos <= 0) | ||
| 161 | break; /* done. */ | ||
| 162 | xlseek(file, pos, SEEK_SET); | ||
| 163 | } | ||
| 164 | |||
| 165 | fflush_stdout_and_exit(EXIT_SUCCESS); | ||
| 166 | } | ||
diff --git a/miscutils/last_fancy.c b/miscutils/last_fancy.c deleted file mode 100644 index e56e0ba85..000000000 --- a/miscutils/last_fancy.c +++ /dev/null | |||
| @@ -1,300 +0,0 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * (sysvinit like) last implementation | ||
| 4 | * | ||
| 5 | * Copyright (C) 2008 by Patricia Muscalu <patricia.muscalu@axis.com> | ||
| 6 | * | ||
| 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include "libbb.h" | ||
| 11 | |||
| 12 | /* NB: ut_name and ut_user are the same field, use only one name (ut_user) | ||
| 13 | * to reduce confusion */ | ||
| 14 | |||
| 15 | #ifndef SHUTDOWN_TIME | ||
| 16 | # define SHUTDOWN_TIME 254 | ||
| 17 | #endif | ||
| 18 | |||
| 19 | #define HEADER_FORMAT "%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n" | ||
| 20 | #define HEADER_LINE "USER", "TTY", \ | ||
| 21 | INET_ADDRSTRLEN, INET_ADDRSTRLEN, "HOST", "LOGIN", " TIME", "" | ||
| 22 | #define HEADER_LINE_WIDE "USER", "TTY", \ | ||
| 23 | INET6_ADDRSTRLEN, INET6_ADDRSTRLEN, "HOST", "LOGIN", " TIME", "" | ||
| 24 | |||
| 25 | #if !defined __UT_LINESIZE && defined UT_LINESIZE | ||
| 26 | # define __UT_LINESIZE UT_LINESIZE | ||
| 27 | #endif | ||
| 28 | |||
| 29 | enum { | ||
| 30 | NORMAL, | ||
| 31 | LOGGED, | ||
| 32 | DOWN, | ||
| 33 | REBOOT, | ||
| 34 | CRASH, | ||
| 35 | GONE | ||
| 36 | }; | ||
| 37 | |||
| 38 | enum { | ||
| 39 | LAST_OPT_W = (1 << 0), /* -W wide */ | ||
| 40 | LAST_OPT_f = (1 << 1), /* -f input file */ | ||
| 41 | LAST_OPT_H = (1 << 2), /* -H header */ | ||
| 42 | }; | ||
| 43 | |||
| 44 | #define show_wide (option_mask32 & LAST_OPT_W) | ||
| 45 | |||
| 46 | static void show_entry(struct utmpx *ut, int state, time_t dur_secs) | ||
| 47 | { | ||
| 48 | unsigned days, hours, mins; | ||
| 49 | char duration[sizeof("(%u+02:02)") + sizeof(int)*3]; | ||
| 50 | char login_time[17]; | ||
| 51 | char logout_time[8]; | ||
| 52 | const char *logout_str; | ||
| 53 | const char *duration_str; | ||
| 54 | time_t tmp; | ||
| 55 | |||
| 56 | /* manpages say ut_tv.tv_sec *is* time_t, | ||
| 57 | * but some systems have it wrong */ | ||
| 58 | tmp = ut->ut_tv.tv_sec; | ||
| 59 | safe_strncpy(login_time, ctime(&tmp), 17); | ||
| 60 | tmp = dur_secs; | ||
| 61 | snprintf(logout_time, 8, "- %s", ctime(&tmp) + 11); | ||
| 62 | |||
| 63 | dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0); | ||
| 64 | /* unsigned int is easier to divide than time_t (which may be signed long) */ | ||
| 65 | mins = dur_secs / 60; | ||
| 66 | days = mins / (24*60); | ||
| 67 | mins = mins % (24*60); | ||
| 68 | hours = mins / 60; | ||
| 69 | mins = mins % 60; | ||
| 70 | |||
| 71 | // if (days) { | ||
| 72 | sprintf(duration, "(%u+%02u:%02u)", days, hours, mins); | ||
| 73 | // } else { | ||
| 74 | // sprintf(duration, " (%02u:%02u)", hours, mins); | ||
| 75 | // } | ||
| 76 | |||
| 77 | logout_str = logout_time; | ||
| 78 | duration_str = duration; | ||
| 79 | switch (state) { | ||
| 80 | case NORMAL: | ||
| 81 | break; | ||
| 82 | case LOGGED: | ||
| 83 | logout_str = " still"; | ||
| 84 | duration_str = "logged in"; | ||
| 85 | break; | ||
| 86 | case DOWN: | ||
| 87 | logout_str = "- down "; | ||
| 88 | break; | ||
| 89 | case REBOOT: | ||
| 90 | break; | ||
| 91 | case CRASH: | ||
| 92 | logout_str = "- crash"; | ||
| 93 | break; | ||
| 94 | case GONE: | ||
| 95 | logout_str = " gone"; | ||
| 96 | duration_str = "- no logout"; | ||
| 97 | break; | ||
| 98 | } | ||
| 99 | |||
| 100 | printf(HEADER_FORMAT, | ||
| 101 | ut->ut_user, | ||
| 102 | ut->ut_line, | ||
| 103 | show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN, | ||
| 104 | show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN, | ||
| 105 | ut->ut_host, | ||
| 106 | login_time, | ||
| 107 | logout_str, | ||
| 108 | duration_str); | ||
| 109 | } | ||
| 110 | |||
| 111 | static int get_ut_type(struct utmpx *ut) | ||
| 112 | { | ||
| 113 | if (ut->ut_line[0] == '~') { | ||
| 114 | if (strcmp(ut->ut_user, "shutdown") == 0) { | ||
| 115 | return SHUTDOWN_TIME; | ||
| 116 | } | ||
| 117 | if (strcmp(ut->ut_user, "reboot") == 0) { | ||
| 118 | return BOOT_TIME; | ||
| 119 | } | ||
| 120 | if (strcmp(ut->ut_user, "runlevel") == 0) { | ||
| 121 | return RUN_LVL; | ||
| 122 | } | ||
| 123 | return ut->ut_type; | ||
| 124 | } | ||
| 125 | |||
| 126 | if (ut->ut_user[0] == 0) { | ||
| 127 | return DEAD_PROCESS; | ||
| 128 | } | ||
| 129 | |||
| 130 | if ((ut->ut_type != DEAD_PROCESS) | ||
| 131 | && (strcmp(ut->ut_user, "LOGIN") != 0) | ||
| 132 | && ut->ut_user[0] | ||
| 133 | && ut->ut_line[0] | ||
| 134 | ) { | ||
| 135 | ut->ut_type = USER_PROCESS; | ||
| 136 | } | ||
| 137 | |||
| 138 | if (strcmp(ut->ut_user, "date") == 0) { | ||
| 139 | if (ut->ut_line[0] == '|') { | ||
| 140 | return OLD_TIME; | ||
| 141 | } | ||
| 142 | if (ut->ut_line[0] == '{') { | ||
| 143 | return NEW_TIME; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | return ut->ut_type; | ||
| 147 | } | ||
| 148 | |||
| 149 | static int is_runlevel_shutdown(struct utmpx *ut) | ||
| 150 | { | ||
| 151 | if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) { | ||
| 152 | return 1; | ||
| 153 | } | ||
| 154 | |||
| 155 | return 0; | ||
| 156 | } | ||
| 157 | |||
| 158 | int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 159 | int last_main(int argc UNUSED_PARAM, char **argv) | ||
| 160 | { | ||
| 161 | struct utmpx ut; | ||
| 162 | const char *filename = _PATH_WTMP; | ||
| 163 | llist_t *zlist; | ||
| 164 | off_t pos; | ||
| 165 | time_t start_time; | ||
| 166 | time_t boot_time; | ||
| 167 | time_t down_time; | ||
| 168 | int file; | ||
| 169 | smallint going_down; | ||
| 170 | smallint boot_down; | ||
| 171 | |||
| 172 | /*opt =*/ getopt32(argv, "Wf:" /* "H" */, &filename); | ||
| 173 | #ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT | ||
| 174 | if (opt & LAST_OPT_H) { | ||
| 175 | /* Print header line */ | ||
| 176 | if (opt & LAST_OPT_W) { | ||
| 177 | printf(HEADER_FORMAT, HEADER_LINE_WIDE); | ||
| 178 | } else { | ||
| 179 | printf(HEADER_FORMAT, HEADER_LINE); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | #endif | ||
| 183 | |||
| 184 | file = xopen(filename, O_RDONLY); | ||
| 185 | { | ||
| 186 | /* in case the file is empty... */ | ||
| 187 | struct stat st; | ||
| 188 | fstat(file, &st); | ||
| 189 | start_time = st.st_ctime; | ||
| 190 | } | ||
| 191 | |||
| 192 | time(&down_time); | ||
| 193 | going_down = 0; | ||
| 194 | boot_down = NORMAL; /* 0 */ | ||
| 195 | zlist = NULL; | ||
| 196 | boot_time = 0; | ||
| 197 | /* get file size, rounding down to last full record */ | ||
| 198 | pos = xlseek(file, 0, SEEK_END) / sizeof(ut) * sizeof(ut); | ||
| 199 | for (;;) { | ||
| 200 | pos -= (off_t)sizeof(ut); | ||
| 201 | if (pos < 0) { | ||
| 202 | /* Beyond the beginning of the file boundary => | ||
| 203 | * the whole file has been read. */ | ||
| 204 | break; | ||
| 205 | } | ||
| 206 | xlseek(file, pos, SEEK_SET); | ||
| 207 | xread(file, &ut, sizeof(ut)); | ||
| 208 | /* rewritten by each record, eventially will have | ||
| 209 | * first record's ut_tv.tv_sec: */ | ||
| 210 | start_time = ut.ut_tv.tv_sec; | ||
| 211 | |||
| 212 | switch (get_ut_type(&ut)) { | ||
| 213 | case SHUTDOWN_TIME: | ||
| 214 | down_time = ut.ut_tv.tv_sec; | ||
| 215 | boot_down = DOWN; | ||
| 216 | going_down = 1; | ||
| 217 | break; | ||
| 218 | case RUN_LVL: | ||
| 219 | if (is_runlevel_shutdown(&ut)) { | ||
| 220 | down_time = ut.ut_tv.tv_sec; | ||
| 221 | going_down = 1; | ||
| 222 | boot_down = DOWN; | ||
| 223 | } | ||
| 224 | break; | ||
| 225 | case BOOT_TIME: | ||
| 226 | strcpy(ut.ut_line, "system boot"); | ||
| 227 | show_entry(&ut, REBOOT, down_time); | ||
| 228 | boot_down = CRASH; | ||
| 229 | going_down = 1; | ||
| 230 | break; | ||
| 231 | case DEAD_PROCESS: | ||
| 232 | if (!ut.ut_line[0]) { | ||
| 233 | break; | ||
| 234 | } | ||
| 235 | /* add_entry */ | ||
| 236 | llist_add_to(&zlist, xmemdup(&ut, sizeof(ut))); | ||
| 237 | break; | ||
| 238 | case USER_PROCESS: { | ||
| 239 | int show; | ||
| 240 | |||
| 241 | if (!ut.ut_line[0]) { | ||
| 242 | break; | ||
| 243 | } | ||
| 244 | /* find_entry */ | ||
| 245 | show = 1; | ||
| 246 | { | ||
| 247 | llist_t *el, *next; | ||
| 248 | for (el = zlist; el; el = next) { | ||
| 249 | struct utmpx *up = (struct utmpx *)el->data; | ||
| 250 | next = el->link; | ||
| 251 | if (strncmp(up->ut_line, ut.ut_line, __UT_LINESIZE) == 0) { | ||
| 252 | if (show) { | ||
| 253 | show_entry(&ut, NORMAL, up->ut_tv.tv_sec); | ||
| 254 | show = 0; | ||
| 255 | } | ||
| 256 | llist_unlink(&zlist, el); | ||
| 257 | free(el->data); | ||
| 258 | free(el); | ||
| 259 | } | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | if (show) { | ||
| 264 | int state = boot_down; | ||
| 265 | |||
| 266 | if (boot_time == 0) { | ||
| 267 | state = LOGGED; | ||
| 268 | /* Check if the process is alive */ | ||
| 269 | if ((ut.ut_pid > 0) | ||
| 270 | && (kill(ut.ut_pid, 0) != 0) | ||
| 271 | && (errno == ESRCH)) { | ||
| 272 | state = GONE; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | show_entry(&ut, state, boot_time); | ||
| 276 | } | ||
| 277 | /* add_entry */ | ||
| 278 | llist_add_to(&zlist, xmemdup(&ut, sizeof(ut))); | ||
| 279 | break; | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | if (going_down) { | ||
| 284 | boot_time = ut.ut_tv.tv_sec; | ||
| 285 | llist_free(zlist, free); | ||
| 286 | zlist = NULL; | ||
| 287 | going_down = 0; | ||
| 288 | } | ||
| 289 | } | ||
| 290 | |||
| 291 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
| 292 | llist_free(zlist, free); | ||
| 293 | } | ||
| 294 | |||
| 295 | printf("\nwtmp begins %s", ctime(&start_time)); | ||
| 296 | |||
| 297 | if (ENABLE_FEATURE_CLEAN_UP) | ||
| 298 | close(file); | ||
| 299 | fflush_stdout_and_exit(EXIT_SUCCESS); | ||
| 300 | } | ||
diff --git a/miscutils/less.c b/miscutils/less.c index 220e2c693..16be1447e 100644 --- a/miscutils/less.c +++ b/miscutils/less.c | |||
| @@ -271,7 +271,7 @@ struct globals { | |||
| 271 | /* flines[] are lines read from stdin, each in malloc'ed buffer. | 271 | /* flines[] are lines read from stdin, each in malloc'ed buffer. |
| 272 | * Line numbers are stored as uint32_t prepended to each line. | 272 | * Line numbers are stored as uint32_t prepended to each line. |
| 273 | * Pointer is adjusted so that flines[i] points directly past | 273 | * Pointer is adjusted so that flines[i] points directly past |
| 274 | * line number. Accesor: */ | 274 | * line number. Accessor: */ |
| 275 | #define MEMPTR(p) ((char*)(p) - 4) | 275 | #define MEMPTR(p) ((char*)(p) - 4) |
| 276 | #define LINENO(p) (*(uint32_t*)((p) - 4)) | 276 | #define LINENO(p) (*(uint32_t*)((p) - 4)) |
| 277 | 277 | ||
diff --git a/miscutils/lsscsi.c b/miscutils/lsscsi.c new file mode 100644 index 000000000..1521680ac --- /dev/null +++ b/miscutils/lsscsi.c | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * lsscsi implementation for busybox | ||
| 4 | * | ||
| 5 | * Copyright (C) 2017 Markus Gothe <nietzsche@lysator.liu.se> | ||
| 6 | * | ||
| 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
| 8 | */ | ||
| 9 | //config:config LSSCSI | ||
| 10 | //config: bool "lsscsi" | ||
| 11 | //config: default y | ||
| 12 | //config: #select PLATFORM_LINUX | ||
| 13 | //config: help | ||
| 14 | //config: lsscsi is a utility for displaying information about SCSI buses in the | ||
| 15 | //config: system and devices connected to them. | ||
| 16 | //config: | ||
| 17 | //config: This version uses sysfs (/sys/bus/scsi/devices) only. | ||
| 18 | |||
| 19 | //applet:IF_LSSCSI(APPLET(lsscsi, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
| 20 | |||
| 21 | //kbuild:lib-$(CONFIG_LSSCSI) += lsscsi.o | ||
| 22 | |||
| 23 | //usage:#define lsscsi_trivial_usage NOUSAGE_STR | ||
| 24 | //usage:#define lsscsi_full_usage "" | ||
| 25 | |||
| 26 | #include "libbb.h" | ||
| 27 | |||
| 28 | static char *get_line(const char *filename, char *buf, unsigned *bufsize_p) | ||
| 29 | { | ||
| 30 | unsigned bufsize = *bufsize_p; | ||
| 31 | ssize_t sz; | ||
| 32 | |||
| 33 | if ((int)(bufsize - 2) <= 0) | ||
| 34 | return buf; | ||
| 35 | |||
| 36 | sz = open_read_close(filename, buf, bufsize - 2); | ||
| 37 | if (sz < 0) | ||
| 38 | sz = 0; | ||
| 39 | buf[sz] = '\0'; | ||
| 40 | trim(buf); | ||
| 41 | |||
| 42 | sz = strlen(buf) + 1; | ||
| 43 | bufsize -= sz; | ||
| 44 | buf += sz; | ||
| 45 | buf[0] = '\0'; | ||
| 46 | |||
| 47 | *bufsize_p = bufsize; | ||
| 48 | return buf; | ||
| 49 | } | ||
| 50 | |||
| 51 | int lsscsi_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 52 | int lsscsi_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | ||
| 53 | { | ||
| 54 | struct dirent *de; | ||
| 55 | DIR *dir; | ||
| 56 | |||
| 57 | xchdir("/sys/bus/scsi/devices"); | ||
| 58 | |||
| 59 | dir = xopendir("."); | ||
| 60 | while ((de = readdir(dir)) != NULL) { | ||
| 61 | char buf[256]; | ||
| 62 | char *ptr; | ||
| 63 | unsigned bufsize; | ||
| 64 | const char *vendor; | ||
| 65 | const char *type_str; | ||
| 66 | const char *type_name; | ||
| 67 | const char *model; | ||
| 68 | const char *rev; | ||
| 69 | unsigned type; | ||
| 70 | |||
| 71 | if (!isdigit(de->d_name[0])) | ||
| 72 | continue; | ||
| 73 | if (!strchr(de->d_name, ':')) | ||
| 74 | continue; | ||
| 75 | if (chdir(de->d_name) != 0) | ||
| 76 | continue; | ||
| 77 | |||
| 78 | bufsize = sizeof(buf); | ||
| 79 | vendor = buf; | ||
| 80 | ptr = get_line("vendor", buf, &bufsize); | ||
| 81 | type_str = ptr; | ||
| 82 | ptr = get_line("type", ptr, &bufsize); | ||
| 83 | model = ptr; | ||
| 84 | ptr = get_line("model", ptr, &bufsize); | ||
| 85 | rev = ptr; | ||
| 86 | ptr = get_line("rev", ptr, &bufsize); | ||
| 87 | |||
| 88 | printf("[%s]\t", de->d_name); | ||
| 89 | |||
| 90 | #define scsi_device_types \ | ||
| 91 | "disk\0" "tape\0" "printer\0" "process\0" \ | ||
| 92 | "worm\0" "\0" "scanner\0" "optical\0" \ | ||
| 93 | "mediumx\0" "comms\0" "\0" "\0" \ | ||
| 94 | "storage\0" "enclosu\0" "sim dsk\0" "opti rd\0" \ | ||
| 95 | "bridge\0" "osd\0" "adi\0" "\0" \ | ||
| 96 | "\0" "\0" "\0" "\0" \ | ||
| 97 | "\0" "\0" "\0" "\0" \ | ||
| 98 | "\0" "\0" "wlun\0" "no dev" | ||
| 99 | type = bb_strtou(type_str, NULL, 10); | ||
| 100 | if (errno | ||
| 101 | || type >= 0x20 | ||
| 102 | || (type_name = nth_string(scsi_device_types, type))[0] == '\0' | ||
| 103 | ) { | ||
| 104 | printf("(%s)\t", type_str); | ||
| 105 | } else { | ||
| 106 | printf("%s\t", type_name); | ||
| 107 | } | ||
| 108 | |||
| 109 | printf("%s\t""%s\t""%s\n", | ||
| 110 | vendor, | ||
| 111 | model, | ||
| 112 | rev | ||
| 113 | ); | ||
| 114 | /* TODO: also output device column, e.g. "/dev/sdX" */ | ||
| 115 | |||
| 116 | xchdir(".."); | ||
| 117 | } | ||
| 118 | |||
| 119 | if (ENABLE_FEATURE_CLEAN_UP) | ||
| 120 | closedir(dir); | ||
| 121 | |||
| 122 | return EXIT_SUCCESS; | ||
| 123 | } | ||
diff --git a/miscutils/mountpoint.c b/miscutils/mountpoint.c deleted file mode 100644 index 8b9e1d779..000000000 --- a/miscutils/mountpoint.c +++ /dev/null | |||
| @@ -1,105 +0,0 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * mountpoint implementation for busybox | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Bernhard Reutner-Fischer | ||
| 6 | * | ||
| 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
| 8 | * | ||
| 9 | * Based on sysvinit's mountpoint | ||
| 10 | */ | ||
| 11 | //config:config MOUNTPOINT | ||
| 12 | //config: bool "mountpoint" | ||
| 13 | //config: default y | ||
| 14 | //config: help | ||
| 15 | //config: mountpoint checks if the directory is a mountpoint. | ||
| 16 | |||
| 17 | //applet:IF_MOUNTPOINT(APPLET(mountpoint, BB_DIR_BIN, BB_SUID_DROP)) | ||
| 18 | |||
| 19 | //kbuild:lib-$(CONFIG_MOUNTPOINT) += mountpoint.o | ||
| 20 | |||
| 21 | //usage:#define mountpoint_trivial_usage | ||
| 22 | //usage: "[-q] <[-dn] DIR | -x DEVICE>" | ||
| 23 | //usage:#define mountpoint_full_usage "\n\n" | ||
| 24 | //usage: "Check if the directory is a mountpoint\n" | ||
| 25 | //usage: "\n -q Quiet" | ||
| 26 | //usage: "\n -d Print major/minor device number of the filesystem" | ||
| 27 | //usage: "\n -n Print device name of the filesystem" | ||
| 28 | //usage: "\n -x Print major/minor device number of the blockdevice" | ||
| 29 | //usage: | ||
| 30 | //usage:#define mountpoint_example_usage | ||
| 31 | //usage: "$ mountpoint /proc\n" | ||
| 32 | //usage: "/proc is not a mountpoint\n" | ||
| 33 | //usage: "$ mountpoint /sys\n" | ||
| 34 | //usage: "/sys is a mountpoint\n" | ||
| 35 | |||
| 36 | #include "libbb.h" | ||
| 37 | |||
| 38 | int mountpoint_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 39 | int mountpoint_main(int argc UNUSED_PARAM, char **argv) | ||
| 40 | { | ||
| 41 | struct stat st; | ||
| 42 | const char *msg; | ||
| 43 | char *arg; | ||
| 44 | int rc, opt; | ||
| 45 | |||
| 46 | opt_complementary = "=1"; /* must have one argument */ | ||
| 47 | opt = getopt32(argv, "qdxn"); | ||
| 48 | #define OPT_q (1) | ||
| 49 | #define OPT_d (2) | ||
| 50 | #define OPT_x (4) | ||
| 51 | #define OPT_n (8) | ||
| 52 | arg = argv[optind]; | ||
| 53 | msg = "%s"; | ||
| 54 | |||
| 55 | rc = (opt & OPT_x) ? stat(arg, &st) : lstat(arg, &st); | ||
| 56 | if (rc != 0) | ||
| 57 | goto err; | ||
| 58 | |||
| 59 | if (opt & OPT_x) { | ||
| 60 | if (S_ISBLK(st.st_mode)) { | ||
| 61 | printf("%u:%u\n", major(st.st_rdev), | ||
| 62 | minor(st.st_rdev)); | ||
| 63 | return EXIT_SUCCESS; | ||
| 64 | } | ||
| 65 | errno = 0; /* make perror_msg work as error_msg */ | ||
| 66 | msg = "%s: not a block device"; | ||
| 67 | goto err; | ||
| 68 | } | ||
| 69 | |||
| 70 | errno = ENOTDIR; | ||
| 71 | if (S_ISDIR(st.st_mode)) { | ||
| 72 | dev_t st_dev = st.st_dev; | ||
| 73 | ino_t st_ino = st.st_ino; | ||
| 74 | char *p = xasprintf("%s/..", arg); | ||
| 75 | |||
| 76 | if (stat(p, &st) == 0) { | ||
| 77 | //int is_mnt = (st_dev != st.st_dev) || (st_dev == st.st_dev && st_ino == st.st_ino); | ||
| 78 | int is_not_mnt = (st_dev == st.st_dev) && (st_ino != st.st_ino); | ||
| 79 | |||
| 80 | if (opt & OPT_d) | ||
| 81 | printf("%u:%u\n", major(st_dev), minor(st_dev)); | ||
| 82 | if (opt & OPT_n) { | ||
| 83 | const char *d = find_block_device(arg); | ||
| 84 | /* name is undefined, but device is mounted -> anonymous superblock! */ | ||
| 85 | /* happens with btrfs */ | ||
| 86 | if (!d) { | ||
| 87 | d = "UNKNOWN"; | ||
| 88 | /* TODO: iterate /proc/mounts, or /proc/self/mountinfo | ||
| 89 | * to find out the device name */ | ||
| 90 | } | ||
| 91 | printf("%s %s\n", d, arg); | ||
| 92 | } | ||
| 93 | if (!(opt & (OPT_q | OPT_d | OPT_n))) | ||
| 94 | printf("%s is %sa mountpoint\n", arg, is_not_mnt ? "not " : ""); | ||
| 95 | return is_not_mnt; | ||
| 96 | } | ||
| 97 | arg = p; | ||
| 98 | /* else: stat had set errno, just fall through */ | ||
| 99 | } | ||
| 100 | |||
| 101 | err: | ||
| 102 | if (!(opt & OPT_q)) | ||
| 103 | bb_perror_msg(msg, arg); | ||
| 104 | return EXIT_FAILURE; | ||
| 105 | } | ||
diff --git a/miscutils/partprobe.c b/miscutils/partprobe.c new file mode 100644 index 000000000..38831598d --- /dev/null +++ b/miscutils/partprobe.c | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2017 Denys Vlasenko <vda.linux@googlemail.com> | ||
| 4 | * | ||
| 5 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
| 6 | */ | ||
| 7 | //config:config PARTPROBE | ||
| 8 | //config: bool "partprobe" | ||
| 9 | //config: default y | ||
| 10 | //config: select PLATFORM_LINUX | ||
| 11 | //config: help | ||
| 12 | //config: Ask kernel to rescan partition table. | ||
| 13 | |||
| 14 | //applet:IF_PARTPROBE(APPLET(partprobe, BB_DIR_USR_SBIN, BB_SUID_DROP)) | ||
| 15 | |||
| 16 | //kbuild:lib-$(CONFIG_PARTPROBE) += partprobe.o | ||
| 17 | |||
| 18 | #include <linux/fs.h> | ||
| 19 | #include "libbb.h" | ||
| 20 | #ifndef BLKRRPART | ||
| 21 | # define BLKRRPART _IO(0x12,95) | ||
| 22 | #endif | ||
| 23 | |||
| 24 | //usage:#define partprobe_trivial_usage | ||
| 25 | //usage: "DEVICE..." | ||
| 26 | //usage:#define partprobe_full_usage "\n\n" | ||
| 27 | //usage: "Ask kernel to rescan partition table" | ||
| 28 | // | ||
| 29 | // partprobe (GNU parted) 3.2: | ||
| 30 | // -d, --dry-run Don't update the kernel | ||
| 31 | // -s, --summary Show a summary of devices and their partitions | ||
| 32 | |||
| 33 | int partprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 34 | int partprobe_main(int argc UNUSED_PARAM, char **argv) | ||
| 35 | { | ||
| 36 | getopt32(argv, ""); | ||
| 37 | argv += optind; | ||
| 38 | |||
| 39 | /* "partprobe" with no arguments just does nothing */ | ||
| 40 | |||
| 41 | while (*argv) { | ||
| 42 | int fd = xopen(*argv, O_RDONLY); | ||
| 43 | /* | ||
| 44 | * Newer versions of parted scan partition tables themselves and | ||
| 45 | * use BLKPG ioctl (BLKPG_DEL_PARTITION / BLKPG_ADD_PARTITION) | ||
| 46 | * since this way kernel does not need to know | ||
| 47 | * partition table formats. | ||
| 48 | * We use good old BLKRRPART: | ||
| 49 | */ | ||
| 50 | ioctl_or_perror_and_die(fd, BLKRRPART, NULL, "%s", *argv); | ||
| 51 | close(fd); | ||
| 52 | argv++; | ||
| 53 | } | ||
| 54 | |||
| 55 | return EXIT_SUCCESS; | ||
| 56 | } | ||
diff --git a/miscutils/setsid.c b/miscutils/setsid.c deleted file mode 100644 index 143a8f8fa..000000000 --- a/miscutils/setsid.c +++ /dev/null | |||
| @@ -1,82 +0,0 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * setsid.c -- execute a command in a new session | ||
| 4 | * Rick Sladkey <jrs@world.std.com> | ||
| 5 | * In the public domain. | ||
| 6 | * | ||
| 7 | * 1999-02-22 Arkadiusz Mickiewicz <misiek@pld.ORG.PL> | ||
| 8 | * - added Native Language Support | ||
| 9 | * | ||
| 10 | * 2001-01-18 John Fremlin <vii@penguinpowered.com> | ||
| 11 | * - fork in case we are process group leader | ||
| 12 | * | ||
| 13 | * 2004-11-12 Paul Fox | ||
| 14 | * - busyboxed | ||
| 15 | */ | ||
| 16 | //config:config SETSID | ||
| 17 | //config: bool "setsid" | ||
| 18 | //config: default y | ||
| 19 | //config: help | ||
| 20 | //config: setsid runs a program in a new session | ||
| 21 | |||
| 22 | //applet:IF_SETSID(APPLET(setsid, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
| 23 | |||
| 24 | //kbuild:lib-$(CONFIG_SETSID) += setsid.o | ||
| 25 | |||
| 26 | //usage:#define setsid_trivial_usage | ||
| 27 | //usage: "[-c] PROG ARGS" | ||
| 28 | //usage:#define setsid_full_usage "\n\n" | ||
| 29 | //usage: "Run PROG in a new session. PROG will have no controlling terminal\n" | ||
| 30 | //usage: "and will not be affected by keyboard signals (^C etc).\n" | ||
| 31 | //usage: "\n -c Set controlling terminal to stdin" | ||
| 32 | |||
| 33 | #include "libbb.h" | ||
| 34 | |||
| 35 | int setsid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 36 | int setsid_main(int argc UNUSED_PARAM, char **argv) | ||
| 37 | { | ||
| 38 | unsigned opt; | ||
| 39 | |||
| 40 | opt_complementary = "-1"; /* at least one arg */ | ||
| 41 | opt = getopt32(argv, "+c"); /* +: stop on first non-opt */ | ||
| 42 | argv += optind; | ||
| 43 | |||
| 44 | /* setsid() is allowed only when we are not a process group leader. | ||
| 45 | * Otherwise our PID serves as PGID of some existing process group | ||
| 46 | * and cannot be used as PGID of a new process group. | ||
| 47 | * | ||
| 48 | * Example: setsid() below fails when run alone in interactive shell: | ||
| 49 | * $ setsid PROG | ||
| 50 | * because shell's child (setsid) is put in a new process group. | ||
| 51 | * But doesn't fail if shell is not interactive | ||
| 52 | * (and therefore doesn't create process groups for pipes), | ||
| 53 | * or if setsid is not the first process in the process group: | ||
| 54 | * $ true | setsid PROG | ||
| 55 | * or if setsid is executed in backquotes (`setsid PROG`)... | ||
| 56 | */ | ||
| 57 | if (setsid() < 0) { | ||
| 58 | pid_t pid = fork_or_rexec(argv); | ||
| 59 | if (pid != 0) { | ||
| 60 | /* parent */ | ||
| 61 | /* TODO: | ||
| 62 | * we can waitpid(pid, &status, 0) and then even | ||
| 63 | * emulate exitcode, making the behavior consistent | ||
| 64 | * in both forked and non forked cases. | ||
| 65 | * However, the code is larger and upstream | ||
| 66 | * does not do such trick. | ||
| 67 | */ | ||
| 68 | return EXIT_SUCCESS; | ||
| 69 | } | ||
| 70 | |||
| 71 | /* child */ | ||
| 72 | /* now there should be no error: */ | ||
| 73 | setsid(); | ||
| 74 | } | ||
| 75 | |||
| 76 | if (opt) { | ||
| 77 | /* -c: set (with stealing) controlling tty */ | ||
| 78 | ioctl(0, TIOCSCTTY, 1); | ||
| 79 | } | ||
| 80 | |||
| 81 | BB_EXECVP_or_die(argv); | ||
| 82 | } | ||
diff --git a/miscutils/taskset.c b/miscutils/taskset.c deleted file mode 100644 index 94a07383a..000000000 --- a/miscutils/taskset.c +++ /dev/null | |||
| @@ -1,221 +0,0 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * taskset - retrieve or set a processes' CPU affinity | ||
| 4 | * Copyright (c) 2006 Bernhard Reutner-Fischer | ||
| 5 | * | ||
| 6 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
| 7 | */ | ||
| 8 | |||
| 9 | //config:config TASKSET | ||
| 10 | //config: bool "taskset" | ||
| 11 | //config: default y | ||
| 12 | //config: help | ||
| 13 | //config: Retrieve or set a processes's CPU affinity. | ||
| 14 | //config: This requires sched_{g,s}etaffinity support in your libc. | ||
| 15 | //config: | ||
| 16 | //config:config FEATURE_TASKSET_FANCY | ||
| 17 | //config: bool "Fancy output" | ||
| 18 | //config: default y | ||
| 19 | //config: depends on TASKSET | ||
| 20 | //config: help | ||
| 21 | //config: Needed for machines with more than 32-64 CPUs: | ||
| 22 | //config: affinity parameter 0xHHHHHHHHHHHHHHHHHHHH can be arbitrarily long | ||
| 23 | //config: in this case. Otherwise, it is limited to sizeof(long). | ||
| 24 | |||
| 25 | //applet:IF_TASKSET(APPLET(taskset, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
| 26 | //kbuild:lib-$(CONFIG_TASKSET) += taskset.o | ||
| 27 | |||
| 28 | //usage:#define taskset_trivial_usage | ||
| 29 | //usage: "[-p] [HEXMASK] PID | PROG ARGS" | ||
| 30 | //usage:#define taskset_full_usage "\n\n" | ||
| 31 | //usage: "Set or get CPU affinity\n" | ||
| 32 | //usage: "\n -p Operate on an existing PID" | ||
| 33 | //usage: | ||
| 34 | //usage:#define taskset_example_usage | ||
| 35 | //usage: "$ taskset 0x7 ./dgemm_test&\n" | ||
| 36 | //usage: "$ taskset -p 0x1 $!\n" | ||
| 37 | //usage: "pid 4790's current affinity mask: 7\n" | ||
| 38 | //usage: "pid 4790's new affinity mask: 1\n" | ||
| 39 | //usage: "$ taskset 0x7 /bin/sh -c './taskset -p 0x1 $$'\n" | ||
| 40 | //usage: "pid 6671's current affinity mask: 1\n" | ||
| 41 | //usage: "pid 6671's new affinity mask: 1\n" | ||
| 42 | //usage: "$ taskset -p 1\n" | ||
| 43 | //usage: "pid 1's current affinity mask: 3\n" | ||
| 44 | /* | ||
| 45 | * Not yet implemented: | ||
| 46 | * -a/--all-tasks (affect all threads) | ||
| 47 | * needs to get TIDs from /proc/PID/task/ and use _them_ as "pid" in sched_setaffinity(pid) | ||
| 48 | * -c/--cpu-list (specify CPUs via "1,3,5-7") | ||
| 49 | */ | ||
| 50 | |||
| 51 | #include <sched.h> | ||
| 52 | #include "libbb.h" | ||
| 53 | |||
| 54 | typedef unsigned long ul; | ||
| 55 | #define SZOF_UL (unsigned)(sizeof(ul)) | ||
| 56 | #define BITS_UL (unsigned)(sizeof(ul)*8) | ||
| 57 | #define MASK_UL (unsigned)(sizeof(ul)*8 - 1) | ||
| 58 | |||
| 59 | #if ENABLE_FEATURE_TASKSET_FANCY | ||
| 60 | #define TASKSET_PRINTF_MASK "%s" | ||
| 61 | /* craft a string from the mask */ | ||
| 62 | static char *from_mask(const ul *mask, unsigned sz_in_bytes) | ||
| 63 | { | ||
| 64 | char *str = xzalloc((sz_in_bytes+1) * 2); /* we will leak it */ | ||
| 65 | char *p = str; | ||
| 66 | for (;;) { | ||
| 67 | ul v = *mask++; | ||
| 68 | if (SZOF_UL == 4) | ||
| 69 | p += sprintf(p, "%08lx", v); | ||
| 70 | if (SZOF_UL == 8) | ||
| 71 | p += sprintf(p, "%016lx", v); | ||
| 72 | if (SZOF_UL == 16) | ||
| 73 | p += sprintf(p, "%032lx", v); /* :) */ | ||
| 74 | sz_in_bytes -= SZOF_UL; | ||
| 75 | if ((int)sz_in_bytes <= 0) | ||
| 76 | break; | ||
| 77 | } | ||
| 78 | while (str[0] == '0' && str[1]) | ||
| 79 | str++; | ||
| 80 | return str; | ||
| 81 | } | ||
| 82 | #else | ||
| 83 | #define TASKSET_PRINTF_MASK "%lx" | ||
| 84 | static unsigned long long from_mask(ul *mask, unsigned sz_in_bytes UNUSED_PARAM) | ||
| 85 | { | ||
| 86 | return *mask; | ||
| 87 | } | ||
| 88 | #endif | ||
| 89 | |||
| 90 | static unsigned long *get_aff(int pid, unsigned *sz) | ||
| 91 | { | ||
| 92 | int r; | ||
| 93 | unsigned long *mask = NULL; | ||
| 94 | unsigned sz_in_bytes = *sz; | ||
| 95 | |||
| 96 | for (;;) { | ||
| 97 | mask = xrealloc(mask, sz_in_bytes); | ||
| 98 | r = sched_getaffinity(pid, sz_in_bytes, (void*)mask); | ||
| 99 | if (r == 0) | ||
| 100 | break; | ||
| 101 | sz_in_bytes *= 2; | ||
| 102 | if (errno == EINVAL && (int)sz_in_bytes > 0) | ||
| 103 | continue; | ||
| 104 | bb_perror_msg_and_die("can't %cet pid %d's affinity", 'g', pid); | ||
| 105 | } | ||
| 106 | //bb_error_msg("get mask[0]:%lx sz_in_bytes:%d", mask[0], sz_in_bytes); | ||
| 107 | *sz = sz_in_bytes; | ||
| 108 | return mask; | ||
| 109 | } | ||
| 110 | |||
| 111 | int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 112 | int taskset_main(int argc UNUSED_PARAM, char **argv) | ||
| 113 | { | ||
| 114 | ul *mask; | ||
| 115 | unsigned mask_size_in_bytes; | ||
| 116 | pid_t pid = 0; | ||
| 117 | unsigned opt_p; | ||
| 118 | const char *current_new; | ||
| 119 | char *aff; | ||
| 120 | |||
| 121 | /* NB: we mimic util-linux's taskset: -p does not take | ||
| 122 | * an argument, i.e., "-pN" is NOT valid, only "-p N"! | ||
| 123 | * Indeed, util-linux-2.13-pre7 uses: | ||
| 124 | * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */ | ||
| 125 | |||
| 126 | opt_complementary = "-1"; /* at least 1 arg */ | ||
| 127 | opt_p = getopt32(argv, "+p"); | ||
| 128 | argv += optind; | ||
| 129 | |||
| 130 | aff = *argv++; | ||
| 131 | if (opt_p) { | ||
| 132 | char *pid_str = aff; | ||
| 133 | if (*argv) { /* "-p <aff> <pid> ...rest.is.ignored..." */ | ||
| 134 | pid_str = *argv; /* NB: *argv != NULL in this case */ | ||
| 135 | } | ||
| 136 | /* else it was just "-p <pid>", and *argv == NULL */ | ||
| 137 | pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1); | ||
| 138 | } else { | ||
| 139 | /* <aff> <cmd...> */ | ||
| 140 | if (!*argv) | ||
| 141 | bb_show_usage(); | ||
| 142 | } | ||
| 143 | |||
| 144 | mask_size_in_bytes = SZOF_UL; | ||
| 145 | current_new = "current"; | ||
| 146 | print_aff: | ||
| 147 | mask = get_aff(pid, &mask_size_in_bytes); | ||
| 148 | if (opt_p) { | ||
| 149 | printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n", | ||
| 150 | pid, current_new, from_mask(mask, mask_size_in_bytes)); | ||
| 151 | if (*argv == NULL) { | ||
| 152 | /* Either it was just "-p <pid>", | ||
| 153 | * or it was "-p <aff> <pid>" and we came here | ||
| 154 | * for the second time (see goto below) */ | ||
| 155 | return EXIT_SUCCESS; | ||
| 156 | } | ||
| 157 | *argv = NULL; | ||
| 158 | current_new = "new"; | ||
| 159 | } | ||
| 160 | memset(mask, 0, mask_size_in_bytes); | ||
| 161 | |||
| 162 | /* Affinity was specified, translate it into mask */ | ||
| 163 | /* it is always in hex, skip "0x" if it exists */ | ||
| 164 | if (aff[0] == '0' && (aff[1]|0x20) == 'x') | ||
| 165 | aff += 2; | ||
| 166 | |||
| 167 | if (!ENABLE_FEATURE_TASKSET_FANCY) { | ||
| 168 | mask[0] = xstrtoul(aff, 16); | ||
| 169 | } else { | ||
| 170 | unsigned i; | ||
| 171 | char *last_char; | ||
| 172 | |||
| 173 | i = 0; /* bit pos in mask[] */ | ||
| 174 | |||
| 175 | /* aff is ASCII hex string, accept very long masks in this form. | ||
| 176 | * Process hex string AABBCCDD... to ulong mask[] | ||
| 177 | * from the rightmost nibble, which is least-significant. | ||
| 178 | * Bits not fitting into mask[] are ignored: (example: 1234 | ||
| 179 | * in 12340000000000000000000000000000000000000ff) | ||
| 180 | */ | ||
| 181 | last_char = strchrnul(aff, '\0'); | ||
| 182 | while (last_char > aff) { | ||
| 183 | char c; | ||
| 184 | ul val; | ||
| 185 | |||
| 186 | last_char--; | ||
| 187 | c = *last_char; | ||
| 188 | if (isdigit(c)) | ||
| 189 | val = c - '0'; | ||
| 190 | else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') | ||
| 191 | val = (c|0x20) - ('a' - 10); | ||
| 192 | else | ||
| 193 | bb_error_msg_and_die("bad affinity '%s'", aff); | ||
| 194 | |||
| 195 | if (i < mask_size_in_bytes * 8) { | ||
| 196 | mask[i / BITS_UL] |= val << (i & MASK_UL); | ||
| 197 | //bb_error_msg("bit %d set", i); | ||
| 198 | } | ||
| 199 | /* else: | ||
| 200 | * We can error out here, but we don't. | ||
| 201 | * For one, kernel itself ignores bits in mask[] | ||
| 202 | * which do not map to any CPUs: | ||
| 203 | * if mask[] has one 32-bit long element, | ||
| 204 | * but you have only 8 CPUs, all bits beyond first 8 | ||
| 205 | * are ignored, silently. | ||
| 206 | * No point in making bits past 31th to be errors. | ||
| 207 | */ | ||
| 208 | i += 4; | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | /* Set pid's or our own (pid==0) affinity */ | ||
| 213 | if (sched_setaffinity(pid, mask_size_in_bytes, (void*)mask)) | ||
| 214 | bb_perror_msg_and_die("can't %cet pid %d's affinity", 's', pid); | ||
| 215 | //bb_error_msg("set mask[0]:%lx", mask[0]); | ||
| 216 | |||
| 217 | if (!argv[0]) /* "-p <aff> <pid> [...ignored...]" */ | ||
| 218 | goto print_aff; /* print new affinity and exit */ | ||
| 219 | |||
| 220 | BB_EXECVP_or_die(argv); | ||
| 221 | } | ||
diff --git a/miscutils/time.c b/miscutils/time.c index a73a837d8..e377bb6b7 100644 --- a/miscutils/time.c +++ b/miscutils/time.c | |||
| @@ -21,10 +21,14 @@ | |||
| 21 | //kbuild:lib-$(CONFIG_TIME) += time.o | 21 | //kbuild:lib-$(CONFIG_TIME) += time.o |
| 22 | 22 | ||
| 23 | //usage:#define time_trivial_usage | 23 | //usage:#define time_trivial_usage |
| 24 | //usage: "[-v] PROG ARGS" | 24 | //usage: "[-vpa] [-o FILE] PROG ARGS" |
| 25 | //usage:#define time_full_usage "\n\n" | 25 | //usage:#define time_full_usage "\n\n" |
| 26 | //usage: "Run PROG, display resource usage when it exits\n" | 26 | //usage: "Run PROG, display resource usage when it exits\n" |
| 27 | //usage: "\n -v Verbose" | 27 | //usage: "\n -v Verbose" |
| 28 | //usage: "\n -p POSIX output format" | ||
| 29 | //usage: "\n -f FMT Custom format" | ||
| 30 | //usage: "\n -o FILE Write result to FILE" | ||
| 31 | //usage: "\n -a Append (else overwrite)" | ||
| 28 | 32 | ||
| 29 | #include "libbb.h" | 33 | #include "libbb.h" |
| 30 | #include <sys/resource.h> /* getrusage */ | 34 | #include <sys/resource.h> /* getrusage */ |
| @@ -397,7 +401,7 @@ static void run_command(char *const *cmd, resource_t *resp) | |||
| 397 | } | 401 | } |
| 398 | 402 | ||
| 399 | /* Have signals kill the child but not self (if possible). */ | 403 | /* Have signals kill the child but not self (if possible). */ |
| 400 | //TODO: just block all sigs? and reenable them in the very end in main? | 404 | //TODO: just block all sigs? and re-enable them in the very end in main? |
| 401 | interrupt_signal = signal(SIGINT, SIG_IGN); | 405 | interrupt_signal = signal(SIGINT, SIG_IGN); |
| 402 | quit_signal = signal(SIGQUIT, SIG_IGN); | 406 | quit_signal = signal(SIGQUIT, SIG_IGN); |
| 403 | 407 | ||
| @@ -412,29 +416,50 @@ int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |||
| 412 | int time_main(int argc UNUSED_PARAM, char **argv) | 416 | int time_main(int argc UNUSED_PARAM, char **argv) |
| 413 | { | 417 | { |
| 414 | resource_t res; | 418 | resource_t res; |
| 415 | const char *output_format = default_format; | 419 | /* $TIME has lowest prio (-v,-p,-f FMT overrride it) */ |
| 420 | const char *output_format = getenv("TIME") ? : default_format; | ||
| 421 | char *output_filename; | ||
| 422 | int output_fd; | ||
| 416 | int opt; | 423 | int opt; |
| 424 | int ex; | ||
| 425 | enum { | ||
| 426 | OPT_v = (1 << 0), | ||
| 427 | OPT_p = (1 << 1), | ||
| 428 | OPT_a = (1 << 2), | ||
| 429 | OPT_o = (1 << 3), | ||
| 430 | OPT_f = (1 << 4), | ||
| 431 | }; | ||
| 417 | 432 | ||
| 418 | opt_complementary = "-1"; /* at least one arg */ | 433 | opt_complementary = "-1"; /* at least one arg */ |
| 419 | /* "+": stop on first non-option */ | 434 | /* "+": stop on first non-option */ |
| 420 | opt = getopt32(argv, "+vp"); | 435 | opt = getopt32(argv, "+vpao:f:", &output_filename, &output_format); |
| 421 | argv += optind; | 436 | argv += optind; |
| 422 | if (opt & 1) | 437 | if (opt & OPT_v) |
| 423 | output_format = long_format; | 438 | output_format = long_format; |
| 424 | if (opt & 2) | 439 | if (opt & OPT_p) |
| 425 | output_format = posix_format; | 440 | output_format = posix_format; |
| 441 | output_fd = STDERR_FILENO; | ||
| 442 | if (opt & OPT_o) { | ||
| 443 | output_fd = xopen(output_filename, | ||
| 444 | (opt & OPT_a) /* append? */ | ||
| 445 | ? (O_CREAT | O_WRONLY | O_CLOEXEC | O_APPEND) | ||
| 446 | : (O_CREAT | O_WRONLY | O_CLOEXEC | O_TRUNC) | ||
| 447 | ); | ||
| 448 | } | ||
| 426 | 449 | ||
| 427 | run_command(argv, &res); | 450 | run_command(argv, &res); |
| 428 | 451 | ||
| 429 | /* Cheat. printf's are shorter :) */ | 452 | /* Cheat. printf's are shorter :) */ |
| 430 | xdup2(STDERR_FILENO, STDOUT_FILENO); | 453 | xdup2(output_fd, STDOUT_FILENO); |
| 431 | summarize(output_format, argv, &res); | 454 | summarize(output_format, argv, &res); |
| 432 | 455 | ||
| 456 | ex = WEXITSTATUS(res.waitstatus); | ||
| 457 | /* Impossible: we do not use WUNTRACED flag in wait()... | ||
| 433 | if (WIFSTOPPED(res.waitstatus)) | 458 | if (WIFSTOPPED(res.waitstatus)) |
| 434 | return WSTOPSIG(res.waitstatus); | 459 | ex = WSTOPSIG(res.waitstatus); |
| 460 | */ | ||
| 435 | if (WIFSIGNALED(res.waitstatus)) | 461 | if (WIFSIGNALED(res.waitstatus)) |
| 436 | return WTERMSIG(res.waitstatus); | 462 | ex = WTERMSIG(res.waitstatus); |
| 437 | if (WIFEXITED(res.waitstatus)) | 463 | |
| 438 | return WEXITSTATUS(res.waitstatus); | 464 | fflush_stdout_and_exit(ex); |
| 439 | fflush_stdout_and_exit(EXIT_SUCCESS); | ||
| 440 | } | 465 | } |
diff --git a/miscutils/timeout.c b/miscutils/timeout.c deleted file mode 100644 index f29dc8a9c..000000000 --- a/miscutils/timeout.c +++ /dev/null | |||
| @@ -1,127 +0,0 @@ | |||
| 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||
| 2 | * COPYING NOTES | ||
| 3 | * | ||
| 4 | * timeout.c -- a timeout handler for shell commands | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005-6, Roberto A. Foglietta <me@roberto.foglietta.name> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; version 2 of the License. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. | ||
| 20 | */ | ||
| 21 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||
| 22 | * REVISION NOTES: | ||
| 23 | * released 17-11-2005 by Roberto A. Foglietta | ||
| 24 | * talarm 04-12-2005 by Roberto A. Foglietta | ||
| 25 | * modified 05-12-2005 by Roberto A. Foglietta | ||
| 26 | * sizerdct 06-12-2005 by Roberto A. Foglietta | ||
| 27 | * splitszf 12-05-2006 by Roberto A. Foglietta | ||
| 28 | * rewrite 14-11-2008 vda | ||
| 29 | */ | ||
| 30 | //config:config TIMEOUT | ||
| 31 | //config: bool "timeout" | ||
| 32 | //config: default y | ||
| 33 | //config: help | ||
| 34 | //config: Runs a program and watches it. If it does not terminate in | ||
| 35 | //config: specified number of seconds, it is sent a signal. | ||
| 36 | |||
| 37 | //applet:IF_TIMEOUT(APPLET(timeout, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
| 38 | |||
| 39 | //kbuild:lib-$(CONFIG_TIMEOUT) += timeout.o | ||
| 40 | |||
| 41 | //usage:#define timeout_trivial_usage | ||
| 42 | //usage: "[-t SECS] [-s SIG] PROG ARGS" | ||
| 43 | //usage:#define timeout_full_usage "\n\n" | ||
| 44 | //usage: "Runs PROG. Sends SIG to it if it is not gone in SECS seconds.\n" | ||
| 45 | //usage: "Defaults: SECS: 10, SIG: TERM." | ||
| 46 | |||
| 47 | #include "libbb.h" | ||
| 48 | |||
| 49 | int timeout_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 50 | int timeout_main(int argc UNUSED_PARAM, char **argv) | ||
| 51 | { | ||
| 52 | int signo; | ||
| 53 | int status; | ||
| 54 | int parent = 0; | ||
| 55 | int timeout = 10; | ||
| 56 | pid_t pid; | ||
| 57 | #if !BB_MMU | ||
| 58 | char *sv1, *sv2; | ||
| 59 | #endif | ||
| 60 | const char *opt_s = "TERM"; | ||
| 61 | |||
| 62 | /* -p option is not documented, it is needed to support NOMMU. */ | ||
| 63 | |||
| 64 | /* -t SECONDS; -p PARENT_PID */ | ||
| 65 | /* '+': stop at first non-option */ | ||
| 66 | getopt32(argv, "+s:t:+" USE_FOR_NOMMU("p:+"), &opt_s, &timeout, &parent); | ||
| 67 | /*argv += optind; - no, wait for bb_daemonize_or_rexec! */ | ||
| 68 | signo = get_signum(opt_s); | ||
| 69 | if (signo < 0) | ||
| 70 | bb_error_msg_and_die("unknown signal '%s'", opt_s); | ||
| 71 | |||
| 72 | /* We want to create a grandchild which will watch | ||
| 73 | * and kill the grandparent. Other methods: | ||
| 74 | * making parent watch child disrupts parent<->child link | ||
| 75 | * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" - | ||
| 76 | * it's better if service_prog is a child of tcpsvd!), | ||
| 77 | * making child watch parent results in programs having | ||
| 78 | * unexpected children. */ | ||
| 79 | |||
| 80 | if (parent) /* we were re-execed, already grandchild */ | ||
| 81 | goto grandchild; | ||
| 82 | if (!argv[optind]) /* no PROG? */ | ||
| 83 | bb_show_usage(); | ||
| 84 | |||
| 85 | #if !BB_MMU | ||
| 86 | sv1 = argv[optind]; | ||
| 87 | sv2 = argv[optind + 1]; | ||
| 88 | #endif | ||
| 89 | pid = xvfork(); | ||
| 90 | if (pid == 0) { | ||
| 91 | /* Child: spawn grandchild and exit */ | ||
| 92 | parent = getppid(); | ||
| 93 | #if !BB_MMU | ||
| 94 | argv[optind] = xasprintf("-p%u", parent); | ||
| 95 | argv[optind + 1] = NULL; | ||
| 96 | #endif | ||
| 97 | /* NB: exits with nonzero on error: */ | ||
| 98 | bb_daemonize_or_rexec(0, argv); | ||
| 99 | /* Here we are grandchild. Sleep, then kill grandparent */ | ||
| 100 | grandchild: | ||
| 101 | /* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */ | ||
| 102 | while (1) { | ||
| 103 | sleep(1); | ||
| 104 | if (--timeout <= 0) | ||
| 105 | break; | ||
| 106 | if (kill(parent, 0)) { | ||
| 107 | /* process is gone */ | ||
| 108 | return EXIT_SUCCESS; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | kill(parent, signo); | ||
| 112 | return EXIT_SUCCESS; | ||
| 113 | } | ||
| 114 | |||
| 115 | /* Parent */ | ||
| 116 | wait(&status); /* wait for child to die */ | ||
| 117 | /* Did intermediate [v]fork or exec fail? */ | ||
| 118 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) | ||
| 119 | return EXIT_FAILURE; | ||
| 120 | /* Ok, exec a program as requested */ | ||
| 121 | argv += optind; | ||
| 122 | #if !BB_MMU | ||
| 123 | argv[0] = sv1; | ||
| 124 | argv[1] = sv2; | ||
| 125 | #endif | ||
| 126 | BB_EXECVP_or_die(argv); | ||
| 127 | } | ||
diff --git a/miscutils/wall.c b/miscutils/wall.c deleted file mode 100644 index 50658f457..000000000 --- a/miscutils/wall.c +++ /dev/null | |||
| @@ -1,63 +0,0 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * wall - write a message to all logged-in users | ||
| 4 | * Copyright (c) 2009 Bernhard Reutner-Fischer | ||
| 5 | * | ||
| 6 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
| 7 | */ | ||
| 8 | |||
| 9 | //config:config WALL | ||
| 10 | //config: bool "wall" | ||
| 11 | //config: default y | ||
| 12 | //config: depends on FEATURE_UTMP | ||
| 13 | //config: help | ||
| 14 | //config: Write a message to all users that are logged in. | ||
| 15 | |||
| 16 | /* Needs to be run by root or be suid root - needs to write to /dev/TTY: */ | ||
| 17 | //applet:IF_WALL(APPLET(wall, BB_DIR_USR_BIN, BB_SUID_REQUIRE)) | ||
| 18 | |||
| 19 | //kbuild:lib-$(CONFIG_WALL) += wall.o | ||
| 20 | |||
| 21 | //usage:#define wall_trivial_usage | ||
| 22 | //usage: "[FILE]" | ||
| 23 | //usage:#define wall_full_usage "\n\n" | ||
| 24 | //usage: "Write content of FILE or stdin to all logged-in users" | ||
| 25 | //usage: | ||
| 26 | //usage:#define wall_sample_usage | ||
| 27 | //usage: "echo foo | wall\n" | ||
| 28 | //usage: "wall ./mymessage" | ||
| 29 | |||
| 30 | #include "libbb.h" | ||
| 31 | |||
| 32 | int wall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 33 | int wall_main(int argc UNUSED_PARAM, char **argv) | ||
| 34 | { | ||
| 35 | struct utmpx *ut; | ||
| 36 | char *msg; | ||
| 37 | int fd; | ||
| 38 | |||
| 39 | fd = STDIN_FILENO; | ||
| 40 | if (argv[1]) { | ||
| 41 | /* The applet is setuid. | ||
| 42 | * Access to the file must be under user's uid/gid. | ||
| 43 | */ | ||
| 44 | fd = xopen_as_uid_gid(argv[1], O_RDONLY, getuid(), getgid()); | ||
| 45 | } | ||
| 46 | msg = xmalloc_read(fd, NULL); | ||
| 47 | if (ENABLE_FEATURE_CLEAN_UP && argv[1]) | ||
| 48 | close(fd); | ||
| 49 | setutxent(); | ||
| 50 | while ((ut = getutxent()) != NULL) { | ||
| 51 | char *line; | ||
| 52 | if (ut->ut_type != USER_PROCESS) | ||
| 53 | continue; | ||
| 54 | line = concat_path_file("/dev", ut->ut_line); | ||
| 55 | xopen_xwrite_close(line, msg); | ||
| 56 | free(line); | ||
| 57 | } | ||
| 58 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
| 59 | endutxent(); | ||
| 60 | free(msg); | ||
| 61 | } | ||
| 62 | return EXIT_SUCCESS; | ||
| 63 | } | ||
