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 /util-linux | |
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 'util-linux')
-rw-r--r-- | util-linux/cal.c | 390 | ||||
-rw-r--r-- | util-linux/chrt.c | 149 | ||||
-rw-r--r-- | util-linux/eject.c | 152 | ||||
-rw-r--r-- | util-linux/fallocate.c | 104 | ||||
-rw-r--r-- | util-linux/fsfreeze.c | 54 | ||||
-rw-r--r-- | util-linux/getopt.c | 7 | ||||
-rw-r--r-- | util-linux/hwclock.c | 2 | ||||
-rw-r--r-- | util-linux/ionice.c | 115 | ||||
-rw-r--r-- | util-linux/last.c | 166 | ||||
-rw-r--r-- | util-linux/last_fancy.c | 300 | ||||
-rw-r--r-- | util-linux/losetup.c | 27 | ||||
-rw-r--r-- | util-linux/mesg.c | 76 | ||||
-rw-r--r-- | util-linux/mount.c | 21 | ||||
-rw-r--r-- | util-linux/mountpoint.c | 105 | ||||
-rw-r--r-- | util-linux/renice.c | 148 | ||||
-rw-r--r-- | util-linux/setsid.c | 82 | ||||
-rw-r--r-- | util-linux/switch_root.c | 12 | ||||
-rw-r--r-- | util-linux/taskset.c | 221 | ||||
-rw-r--r-- | util-linux/umount.c | 24 | ||||
-rw-r--r-- | util-linux/volume_id/udf.c | 2 | ||||
-rw-r--r-- | util-linux/wall.c | 63 |
21 files changed, 2189 insertions, 31 deletions
diff --git a/util-linux/cal.c b/util-linux/cal.c new file mode 100644 index 000000000..8196619b0 --- /dev/null +++ b/util-linux/cal.c | |||
@@ -0,0 +1,390 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Calendar implementation for busybox | ||
4 | * | ||
5 | * See original copyright at the end of this file | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
8 | */ | ||
9 | /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) | ||
10 | * | ||
11 | * Major size reduction... over 50% (>1.5k) on i386. | ||
12 | */ | ||
13 | //config:config CAL | ||
14 | //config: bool "cal" | ||
15 | //config: default y | ||
16 | //config: help | ||
17 | //config: cal is used to display a monthly calendar. | ||
18 | |||
19 | //applet:IF_CAL(APPLET(cal, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
20 | |||
21 | //kbuild:lib-$(CONFIG_CAL) += cal.o | ||
22 | |||
23 | /* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */ | ||
24 | /* BB_AUDIT BUG: The output of 'cal -j 1752' is incorrect. The upstream | ||
25 | * BB_AUDIT BUG: version in util-linux seems to be broken as well. */ | ||
26 | /* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */ | ||
27 | |||
28 | //usage:#define cal_trivial_usage | ||
29 | //usage: "[-jy] [[MONTH] YEAR]" | ||
30 | //usage:#define cal_full_usage "\n\n" | ||
31 | //usage: "Display a calendar\n" | ||
32 | //usage: "\n -j Use julian dates" | ||
33 | //usage: "\n -y Display the entire year" | ||
34 | |||
35 | #include "libbb.h" | ||
36 | #include "unicode.h" | ||
37 | |||
38 | /* We often use "unsigned" instead of "int", it's easier to div on most CPUs */ | ||
39 | |||
40 | #define THURSDAY 4 /* for reformation */ | ||
41 | #define SATURDAY 6 /* 1 Jan 1 was a Saturday */ | ||
42 | |||
43 | #define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */ | ||
44 | #define NUMBER_MISSING_DAYS 11 /* 11 day correction */ | ||
45 | |||
46 | #define MAXDAYS 42 /* max slots in a month array */ | ||
47 | #define SPACE -1 /* used in day array */ | ||
48 | |||
49 | static const unsigned char days_in_month[] ALIGN1 = { | ||
50 | 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||
51 | }; | ||
52 | |||
53 | static const unsigned char sep1752[] ALIGN1 = { | ||
54 | 1, 2, 14, 15, 16, | ||
55 | 17, 18, 19, 20, 21, 22, 23, | ||
56 | 24, 25, 26, 27, 28, 29, 30 | ||
57 | }; | ||
58 | |||
59 | /* Set to 0 or 1 in main */ | ||
60 | #define julian ((unsigned)option_mask32) | ||
61 | |||
62 | /* leap year -- account for Gregorian reformation in 1752 */ | ||
63 | static int leap_year(unsigned yr) | ||
64 | { | ||
65 | if (yr <= 1752) | ||
66 | return !(yr % 4); | ||
67 | return (!(yr % 4) && (yr % 100)) || !(yr % 400); | ||
68 | } | ||
69 | |||
70 | /* number of centuries since 1700, not inclusive */ | ||
71 | #define centuries_since_1700(yr) \ | ||
72 | ((yr) > 1700 ? (yr) / 100 - 17 : 0) | ||
73 | |||
74 | /* number of centuries since 1700 whose modulo of 400 is 0 */ | ||
75 | #define quad_centuries_since_1700(yr) \ | ||
76 | ((yr) > 1600 ? ((yr) - 1600) / 400 : 0) | ||
77 | |||
78 | /* number of leap years between year 1 and this year, not inclusive */ | ||
79 | #define leap_years_since_year_1(yr) \ | ||
80 | ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr)) | ||
81 | |||
82 | static void center(char *, unsigned, unsigned); | ||
83 | static void day_array(unsigned, unsigned, unsigned *); | ||
84 | static void trim_trailing_spaces_and_print(char *); | ||
85 | |||
86 | static void blank_string(char *buf, size_t buflen); | ||
87 | static char *build_row(char *p, unsigned *dp); | ||
88 | |||
89 | #define DAY_LEN 3 /* 3 spaces per day */ | ||
90 | #define J_DAY_LEN (DAY_LEN + 1) | ||
91 | #define WEEK_LEN 20 /* 7 * 3 - one space at the end */ | ||
92 | #define J_WEEK_LEN (WEEK_LEN + 7) | ||
93 | #define HEAD_SEP 2 /* spaces between day headings */ | ||
94 | |||
95 | int cal_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
96 | int cal_main(int argc UNUSED_PARAM, char **argv) | ||
97 | { | ||
98 | struct tm zero_tm; | ||
99 | time_t now; | ||
100 | unsigned month, year, flags, i; | ||
101 | char *month_names[12]; | ||
102 | /* normal heading: */ | ||
103 | /* "Su Mo Tu We Th Fr Sa" */ | ||
104 | /* -j heading: */ | ||
105 | /* " Su Mo Tu We Th Fr Sa" */ | ||
106 | char day_headings[ENABLE_UNICODE_SUPPORT ? 28 * 6 : 28]; | ||
107 | IF_UNICODE_SUPPORT(char *hp = day_headings;) | ||
108 | char buf[40]; | ||
109 | |||
110 | init_unicode(); | ||
111 | |||
112 | flags = getopt32(argv, "jy"); | ||
113 | /* This sets julian = flags & 1: */ | ||
114 | option_mask32 &= 1; | ||
115 | month = 0; | ||
116 | argv += optind; | ||
117 | |||
118 | if (!argv[0]) { | ||
119 | struct tm *ptm; | ||
120 | |||
121 | time(&now); | ||
122 | ptm = localtime(&now); | ||
123 | year = ptm->tm_year + 1900; | ||
124 | if (!(flags & 2)) { /* no -y */ | ||
125 | month = ptm->tm_mon + 1; | ||
126 | } | ||
127 | } else { | ||
128 | if (argv[1]) { | ||
129 | if (argv[2]) { | ||
130 | bb_show_usage(); | ||
131 | } | ||
132 | if (!(flags & 2)) { /* no -y */ | ||
133 | month = xatou_range(*argv, 1, 12); | ||
134 | } | ||
135 | argv++; | ||
136 | } | ||
137 | year = xatou_range(*argv, 1, 9999); | ||
138 | } | ||
139 | |||
140 | blank_string(day_headings, sizeof(day_headings) - 7 + 7*julian); | ||
141 | |||
142 | i = 0; | ||
143 | do { | ||
144 | zero_tm.tm_mon = i; | ||
145 | /* full month name according to locale */ | ||
146 | strftime(buf, sizeof(buf), "%B", &zero_tm); | ||
147 | month_names[i] = xstrdup(buf); | ||
148 | |||
149 | if (i < 7) { | ||
150 | zero_tm.tm_wday = i; | ||
151 | /* abbreviated weekday name according to locale */ | ||
152 | strftime(buf, sizeof(buf), "%a", &zero_tm); | ||
153 | #if ENABLE_UNICODE_SUPPORT | ||
154 | if (julian) | ||
155 | *hp++ = ' '; | ||
156 | { | ||
157 | char *two_wchars = unicode_conv_to_printable_fixedwidth(/*NULL,*/ buf, 2); | ||
158 | strcpy(hp, two_wchars); | ||
159 | free(two_wchars); | ||
160 | } | ||
161 | hp += strlen(hp); | ||
162 | *hp++ = ' '; | ||
163 | #else | ||
164 | strncpy(day_headings + i * (3+julian) + julian, buf, 2); | ||
165 | #endif | ||
166 | } | ||
167 | } while (++i < 12); | ||
168 | IF_UNICODE_SUPPORT(hp[-1] = '\0';) | ||
169 | |||
170 | if (month) { | ||
171 | unsigned row, len, days[MAXDAYS]; | ||
172 | unsigned *dp = days; | ||
173 | char lineout[30]; | ||
174 | |||
175 | day_array(month, year, dp); | ||
176 | len = sprintf(lineout, "%s %u", month_names[month - 1], year); | ||
177 | printf("%*s%s\n%s\n", | ||
178 | ((7*julian + WEEK_LEN) - len) / 2, "", | ||
179 | lineout, day_headings); | ||
180 | for (row = 0; row < 6; row++) { | ||
181 | build_row(lineout, dp)[0] = '\0'; | ||
182 | dp += 7; | ||
183 | trim_trailing_spaces_and_print(lineout); | ||
184 | } | ||
185 | } else { | ||
186 | unsigned row, which_cal, week_len, days[12][MAXDAYS]; | ||
187 | unsigned *dp; | ||
188 | char lineout[80]; | ||
189 | |||
190 | sprintf(lineout, "%u", year); | ||
191 | center(lineout, | ||
192 | (WEEK_LEN * 3 + HEAD_SEP * 2) | ||
193 | + julian * (J_WEEK_LEN * 2 + HEAD_SEP | ||
194 | - (WEEK_LEN * 3 + HEAD_SEP * 2)), | ||
195 | 0 | ||
196 | ); | ||
197 | puts("\n"); /* two \n's */ | ||
198 | for (i = 0; i < 12; i++) { | ||
199 | day_array(i + 1, year, days[i]); | ||
200 | } | ||
201 | blank_string(lineout, sizeof(lineout)); | ||
202 | week_len = WEEK_LEN + julian * (J_WEEK_LEN - WEEK_LEN); | ||
203 | for (month = 0; month < 12; month += 3-julian) { | ||
204 | center(month_names[month], week_len, HEAD_SEP); | ||
205 | if (!julian) { | ||
206 | center(month_names[month + 1], week_len, HEAD_SEP); | ||
207 | } | ||
208 | center(month_names[month + 2 - julian], week_len, 0); | ||
209 | printf("\n%s%*s%s", day_headings, HEAD_SEP, "", day_headings); | ||
210 | if (!julian) { | ||
211 | printf("%*s%s", HEAD_SEP, "", day_headings); | ||
212 | } | ||
213 | bb_putchar('\n'); | ||
214 | for (row = 0; row < (6*7); row += 7) { | ||
215 | for (which_cal = 0; which_cal < 3-julian; which_cal++) { | ||
216 | dp = days[month + which_cal] + row; | ||
217 | build_row(lineout + which_cal * (week_len + 2), dp); | ||
218 | } | ||
219 | /* blank_string took care of nul termination. */ | ||
220 | trim_trailing_spaces_and_print(lineout); | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | |||
225 | fflush_stdout_and_exit(EXIT_SUCCESS); | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * day_array -- | ||
230 | * Fill in an array of 42 integers with a calendar. Assume for a moment | ||
231 | * that you took the (maximum) 6 rows in a calendar and stretched them | ||
232 | * out end to end. You would have 42 numbers or spaces. This routine | ||
233 | * builds that array for any month from Jan. 1 through Dec. 9999. | ||
234 | */ | ||
235 | static void day_array(unsigned month, unsigned year, unsigned *days) | ||
236 | { | ||
237 | unsigned long temp; | ||
238 | unsigned i; | ||
239 | unsigned day, dw, dm; | ||
240 | |||
241 | memset(days, SPACE, MAXDAYS * sizeof(int)); | ||
242 | |||
243 | if ((month == 9) && (year == 1752)) { | ||
244 | /* Assumes the Gregorian reformation eliminates | ||
245 | * 3 Sep. 1752 through 13 Sep. 1752. | ||
246 | */ | ||
247 | unsigned j_offset = julian * 244; | ||
248 | size_t oday = 0; | ||
249 | |||
250 | do { | ||
251 | days[oday+2] = sep1752[oday] + j_offset; | ||
252 | } while (++oday < sizeof(sep1752)); | ||
253 | |||
254 | return; | ||
255 | } | ||
256 | |||
257 | /* day_in_year | ||
258 | * return the 1 based day number within the year | ||
259 | */ | ||
260 | day = 1; | ||
261 | if ((month > 2) && leap_year(year)) { | ||
262 | ++day; | ||
263 | } | ||
264 | |||
265 | i = month; | ||
266 | while (i) { | ||
267 | day += days_in_month[--i]; | ||
268 | } | ||
269 | |||
270 | /* day_in_week | ||
271 | * return the 0 based day number for any date from 1 Jan. 1 to | ||
272 | * 31 Dec. 9999. Assumes the Gregorian reformation eliminates | ||
273 | * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all | ||
274 | * missing days. | ||
275 | */ | ||
276 | temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) + day; | ||
277 | if (temp < FIRST_MISSING_DAY) { | ||
278 | dw = ((temp - 1 + SATURDAY) % 7); | ||
279 | } else { | ||
280 | dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); | ||
281 | } | ||
282 | |||
283 | if (!julian) { | ||
284 | day = 1; | ||
285 | } | ||
286 | |||
287 | dm = days_in_month[month]; | ||
288 | if ((month == 2) && leap_year(year)) { | ||
289 | ++dm; | ||
290 | } | ||
291 | |||
292 | do { | ||
293 | days[dw++] = day++; | ||
294 | } while (--dm); | ||
295 | } | ||
296 | |||
297 | static void trim_trailing_spaces_and_print(char *s) | ||
298 | { | ||
299 | char *p = s; | ||
300 | |||
301 | while (*p) { | ||
302 | ++p; | ||
303 | } | ||
304 | while (p != s) { | ||
305 | --p; | ||
306 | if (!isspace(*p)) { | ||
307 | p[1] = '\0'; | ||
308 | break; | ||
309 | } | ||
310 | } | ||
311 | |||
312 | puts(s); | ||
313 | } | ||
314 | |||
315 | static void center(char *str, unsigned len, unsigned separate) | ||
316 | { | ||
317 | unsigned n = strlen(str); | ||
318 | len -= n; | ||
319 | printf("%*s%*s", (len/2) + n, str, (len/2) + (len % 2) + separate, ""); | ||
320 | } | ||
321 | |||
322 | static void blank_string(char *buf, size_t buflen) | ||
323 | { | ||
324 | memset(buf, ' ', buflen); | ||
325 | buf[buflen-1] = '\0'; | ||
326 | } | ||
327 | |||
328 | static char *build_row(char *p, unsigned *dp) | ||
329 | { | ||
330 | unsigned col, val, day; | ||
331 | |||
332 | memset(p, ' ', (julian + DAY_LEN) * 7); | ||
333 | |||
334 | col = 0; | ||
335 | do { | ||
336 | day = *dp++; | ||
337 | if (day != SPACE) { | ||
338 | if (julian) { | ||
339 | ++p; | ||
340 | if (day >= 100) { | ||
341 | *p = '0'; | ||
342 | p[-1] = (day / 100) + '0'; | ||
343 | day %= 100; | ||
344 | } | ||
345 | } | ||
346 | val = day / 10; | ||
347 | if (val > 0) { | ||
348 | *p = val + '0'; | ||
349 | } | ||
350 | *++p = day % 10 + '0'; | ||
351 | p += 2; | ||
352 | } else { | ||
353 | p += DAY_LEN + julian; | ||
354 | } | ||
355 | } while (++col < 7); | ||
356 | |||
357 | return p; | ||
358 | } | ||
359 | |||
360 | /* | ||
361 | * Copyright (c) 1989, 1993, 1994 | ||
362 | * The Regents of the University of California. All rights reserved. | ||
363 | * | ||
364 | * This code is derived from software contributed to Berkeley by | ||
365 | * Kim Letkeman. | ||
366 | * | ||
367 | * Redistribution and use in source and binary forms, with or without | ||
368 | * modification, are permitted provided that the following conditions | ||
369 | * are met: | ||
370 | * 1. Redistributions of source code must retain the above copyright | ||
371 | * notice, this list of conditions and the following disclaimer. | ||
372 | * 2. Redistributions in binary form must reproduce the above copyright | ||
373 | * notice, this list of conditions and the following disclaimer in the | ||
374 | * documentation and/or other materials provided with the distribution. | ||
375 | * 3. Neither the name of the University nor the names of its contributors | ||
376 | * may be used to endorse or promote products derived from this software | ||
377 | * without specific prior written permission. | ||
378 | * | ||
379 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
380 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
381 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
382 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
383 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
384 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
385 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
386 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
387 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
388 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
389 | * SUCH DAMAGE. | ||
390 | */ | ||
diff --git a/util-linux/chrt.c b/util-linux/chrt.c new file mode 100644 index 000000000..1604a6890 --- /dev/null +++ b/util-linux/chrt.c | |||
@@ -0,0 +1,149 @@ | |||
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/util-linux/eject.c b/util-linux/eject.c new file mode 100644 index 000000000..667932f6c --- /dev/null +++ b/util-linux/eject.c | |||
@@ -0,0 +1,152 @@ | |||
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/util-linux/fallocate.c b/util-linux/fallocate.c new file mode 100644 index 000000000..1cd851bde --- /dev/null +++ b/util-linux/fallocate.c | |||
@@ -0,0 +1,104 @@ | |||
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 | |||
8 | //config:config FALLOCATE | ||
9 | //config: bool "fallocate" | ||
10 | //config: default y | ||
11 | //config: help | ||
12 | //config: Preallocate space for files. | ||
13 | |||
14 | //applet:IF_FALLOCATE(APPLET(fallocate, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
15 | |||
16 | //kbuild:lib-$(CONFIG_FALLOCATE) += fallocate.o | ||
17 | |||
18 | //usage:#define fallocate_trivial_usage | ||
19 | //usage: "[-o OFS] -l LEN FILE" | ||
20 | // fallocate [-c|-p|-z] [-n] [-o OFS] -l LEN FILE | ||
21 | // fallocate -d [-o OFS] [-l LEN] FILE | ||
22 | //usage:#define fallocate_full_usage "\n\n" | ||
23 | //usage: "Preallocate space for FILE\n" | ||
24 | // "\n -c Remove range" | ||
25 | // "\n -p Make hole" | ||
26 | // "\n -z Zero and allocate range" | ||
27 | // "\n -d Convert zeros to holes" | ||
28 | // "\n -n Keep size" | ||
29 | //usage: "\n -o OFS Offset of range" | ||
30 | //usage: "\n -l LEN Length of range" | ||
31 | |||
32 | //Upstream options: | ||
33 | //The options --collapse-range, --dig-holes, --punch-hole and --zero-range | ||
34 | //are mutually exclusive. | ||
35 | //-c, --collapse-range | ||
36 | // Removes a byte range from a file, without leaving a hole. The byte range | ||
37 | // to be collapsed starts at offset and continues for length bytes. | ||
38 | // At the completion of the operation, the contents of the file starting | ||
39 | // at the location offset+length will be appended at the location offset, | ||
40 | // and the file will be length bytes smaller. The option --keep-size may | ||
41 | // not be specified for the collapse-range operation. | ||
42 | //-d, --dig-holes | ||
43 | // Detect and dig holes. This makes the file sparse in-place, without using | ||
44 | // extra disk space. The minimum size of the hole depends on filesystem I/O | ||
45 | // block size (usually 4096 bytes). Also, | ||
46 | //-l, --length length | ||
47 | // Specifies the length of the range, in bytes. | ||
48 | //-n, --keep-size | ||
49 | // Do not modify the apparent length of the file. This may effectively | ||
50 | // allocate blocks past EOF, which can be removed with a truncate. | ||
51 | //-o, --offset offset | ||
52 | // Specifies the beginning offset of the range, in bytes. | ||
53 | //-p, --punch-hole | ||
54 | // Deallocates space (i.e., creates a hole) in the byte range starting | ||
55 | // at offset and continuing for length bytes. Within the specified range, | ||
56 | // partial filesystem blocks are zeroed, and whole | ||
57 | // filesystem blocks are removed from the file. After a successful call, | ||
58 | // subsequent reads from this range will return zeroes. This option may not | ||
59 | // be specified at the same time as the | ||
60 | // --zero-range option. Also, when using this option, --keep-size is implied. | ||
61 | //-z, --zero-range | ||
62 | // Zeroes space in the byte range starting at offset and continuing for | ||
63 | // length bytes. Within the specified range, blocks are preallocated for | ||
64 | // the regions that span the holes in the file. After | ||
65 | // a successful call, subsequent reads from this range will return zeroes. | ||
66 | // Zeroing is done within the filesystem preferably by converting the range | ||
67 | // into unwritten extents. This approach means that the specified range | ||
68 | // will not be physically zeroed out on the device (except for partial | ||
69 | // blocks at the either end of the range), and I/O is (otherwise) required | ||
70 | // only to update metadata. | ||
71 | // Option --keep-size can be specified to prevent file length modification. | ||
72 | |||
73 | #include "libbb.h" | ||
74 | |||
75 | int fallocate_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
76 | int fallocate_main(int argc UNUSED_PARAM, char **argv) | ||
77 | { | ||
78 | const char *str_l; | ||
79 | const char *str_o = "0"; | ||
80 | off_t ofs, len; | ||
81 | unsigned opts; | ||
82 | int fd; | ||
83 | |||
84 | /* exactly one non-option arg */ | ||
85 | opt_complementary = "=1"; | ||
86 | opts = getopt32(argv, "l:o:", &str_l, &str_o); | ||
87 | if (!(opts & 1)) | ||
88 | bb_show_usage(); | ||
89 | |||
90 | ofs = xatoull_sfx(str_o, kmg_i_suffixes); | ||
91 | len = xatoull_sfx(str_l, kmg_i_suffixes); | ||
92 | |||
93 | argv += optind; | ||
94 | fd = xopen3(*argv, O_RDWR | O_CREAT, 0666); | ||
95 | |||
96 | /* posix_fallocate has unusual method of returning error */ | ||
97 | /* maybe use Linux-specific fallocate(int fd, int mode, off_t offset, off_t len) instead? */ | ||
98 | if ((errno = posix_fallocate(fd, ofs, len)) != 0) | ||
99 | bb_perror_msg_and_die("fallocate '%s'", *argv); | ||
100 | |||
101 | /* util-linux also performs fsync(fd); */ | ||
102 | |||
103 | return EXIT_SUCCESS; | ||
104 | } | ||
diff --git a/util-linux/fsfreeze.c b/util-linux/fsfreeze.c new file mode 100644 index 000000000..70dec24ec --- /dev/null +++ b/util-linux/fsfreeze.c | |||
@@ -0,0 +1,54 @@ | |||
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 | |||
8 | //config:config FSFREEZE | ||
9 | //config: bool "fsfreeze" | ||
10 | //config: default y | ||
11 | //config: select PLATFORM_LINUX | ||
12 | //config: select LONG_OPTS | ||
13 | //config: help | ||
14 | //config: Halt new accesses and flush writes on a mounted filesystem. | ||
15 | |||
16 | //applet:IF_FSFREEZE(APPLET(fsfreeze, BB_DIR_USR_SBIN, BB_SUID_DROP)) | ||
17 | |||
18 | //kbuild:lib-$(CONFIG_FSFREEZE) += fsfreeze.o | ||
19 | |||
20 | //usage:#define fsfreeze_trivial_usage | ||
21 | //usage: "--[un]freeze MOUNTPOINT" | ||
22 | //usage:#define fsfreeze_full_usage "\n\n" | ||
23 | //usage: "Flush and halt writes to MOUNTPOINT" | ||
24 | |||
25 | #include "libbb.h" | ||
26 | #include <linux/fs.h> | ||
27 | |||
28 | #ifndef FIFREEZE | ||
29 | # define FIFREEZE _IOWR('X', 119, int) | ||
30 | # define FITHAW _IOWR('X', 120, int) | ||
31 | #endif | ||
32 | |||
33 | int fsfreeze_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
34 | int fsfreeze_main(int argc UNUSED_PARAM, char **argv) | ||
35 | { | ||
36 | unsigned opts; | ||
37 | int fd; | ||
38 | |||
39 | applet_long_options = | ||
40 | "freeze\0" No_argument "\xff" | ||
41 | "unfreeze\0" No_argument "\xfe" | ||
42 | ; | ||
43 | /* exactly one non-option arg: the mountpoint */ | ||
44 | /* one of opts is required */ | ||
45 | /* opts are mutually exclusive */ | ||
46 | opt_complementary = "=1:""\xff:\xfe:""\xff--\xfe:\xfe--\xff"; | ||
47 | opts = getopt32(argv, ""); | ||
48 | |||
49 | fd = xopen(argv[optind], O_RDONLY); | ||
50 | /* Works with NULL arg on linux-4.8.0 */ | ||
51 | xioctl(fd, (opts & 1) ? FIFREEZE : FITHAW, NULL); | ||
52 | |||
53 | return EXIT_SUCCESS; | ||
54 | } | ||
diff --git a/util-linux/getopt.c b/util-linux/getopt.c index 63294c520..79d54854b 100644 --- a/util-linux/getopt.c +++ b/util-linux/getopt.c | |||
@@ -246,12 +246,7 @@ static int generate_output(char **argv, int argc, const char *optstr, const stru | |||
246 | 246 | ||
247 | /* We used it already in main() in getopt32(), | 247 | /* We used it already in main() in getopt32(), |
248 | * we *must* reset getopt(3): */ | 248 | * we *must* reset getopt(3): */ |
249 | #ifdef __GLIBC__ | 249 | GETOPT_RESET(); |
250 | optind = 0; | ||
251 | #else /* BSD style */ | ||
252 | optind = 1; | ||
253 | /* optreset = 1; */ | ||
254 | #endif | ||
255 | 250 | ||
256 | while (1) { | 251 | while (1) { |
257 | #if ENABLE_FEATURE_GETOPT_LONG | 252 | #if ENABLE_FEATURE_GETOPT_LONG |
diff --git a/util-linux/hwclock.c b/util-linux/hwclock.c index d65011a71..8cb908cb3 100644 --- a/util-linux/hwclock.c +++ b/util-linux/hwclock.c | |||
@@ -167,7 +167,7 @@ static void from_sys_clock(const char **pp_rtcname, int utc) | |||
167 | * On x86, even though code does set hw clock within <1ms of exact | 167 | * On x86, even though code does set hw clock within <1ms of exact |
168 | * whole seconds, apparently hw clock (at least on some machines) | 168 | * whole seconds, apparently hw clock (at least on some machines) |
169 | * doesn't reset internal fractional seconds to 0, | 169 | * doesn't reset internal fractional seconds to 0, |
170 | * making all this a pointless excercise. | 170 | * making all this a pointless exercise. |
171 | */ | 171 | */ |
172 | /* If we see that we are N usec away from whole second, | 172 | /* If we see that we are N usec away from whole second, |
173 | * we'll sleep for N-ADJ usecs. ADJ corrects for the fact | 173 | * we'll sleep for N-ADJ usecs. ADJ corrects for the fact |
diff --git a/util-linux/ionice.c b/util-linux/ionice.c new file mode 100644 index 000000000..c54b3a6e1 --- /dev/null +++ b/util-linux/ionice.c | |||
@@ -0,0 +1,115 @@ | |||
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/util-linux/last.c b/util-linux/last.c new file mode 100644 index 000000000..b3f125c3f --- /dev/null +++ b/util-linux/last.c | |||
@@ -0,0 +1,166 @@ | |||
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/util-linux/last_fancy.c b/util-linux/last_fancy.c new file mode 100644 index 000000000..e56e0ba85 --- /dev/null +++ b/util-linux/last_fancy.c | |||
@@ -0,0 +1,300 @@ | |||
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/util-linux/losetup.c b/util-linux/losetup.c index 4424d9cbb..d356f49c2 100644 --- a/util-linux/losetup.c +++ b/util-linux/losetup.c | |||
@@ -127,12 +127,37 @@ int losetup_main(int argc UNUSED_PARAM, char **argv) | |||
127 | d = *argv++; | 127 | d = *argv++; |
128 | 128 | ||
129 | if (argv[0]) { | 129 | if (argv[0]) { |
130 | if (set_loop(&d, argv[0], offset, (opt & OPT_r)) < 0) | 130 | if (set_loop(&d, argv[0], offset, (opt & OPT_r) ? BB_LO_FLAGS_READ_ONLY : 0) < 0) |
131 | bb_simple_perror_msg_and_die(argv[0]); | 131 | bb_simple_perror_msg_and_die(argv[0]); |
132 | return EXIT_SUCCESS; | 132 | return EXIT_SUCCESS; |
133 | } | 133 | } |
134 | } | 134 | } |
135 | 135 | ||
136 | /* TODO: util-linux 2.28 shows this when run w/o params: | ||
137 | * NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE DIO | ||
138 | * /dev/loop0 0 0 1 0 /PATH/TO/FILE 0 | ||
139 | * | ||
140 | * implemented by reading /sys: | ||
141 | * | ||
142 | * open("/sys/block", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3 | ||
143 | * newfstatat(3, "loop0/loop/backing_file", {st_mode=S_IFREG|0444, st_size=4096, ...}, 0) = 0 | ||
144 | * stat("/dev/loop0", {st_mode=S_IFBLK|0660, st_rdev=makedev(7, 0), ...}) = 0 | ||
145 | * open("/sys/dev/block/7:0/loop/offset", O_RDONLY|O_CLOEXEC) = 5 | ||
146 | * read(5, "0\n", 4096) = 2 | ||
147 | * open("/sys/dev/block/7:0/loop/sizelimit", O_RDONLY|O_CLOEXEC) = 5 | ||
148 | * read(5, "0\n", 4096) = 2 | ||
149 | * open("/sys/dev/block/7:0/loop/offset", O_RDONLY|O_CLOEXEC) = 5 | ||
150 | * read(5, "0\n", 4096) = 2 | ||
151 | * open("/sys/dev/block/7:0/loop/autoclear", O_RDONLY|O_CLOEXEC) = 5 | ||
152 | * read(5, "1\n", 4096) = 2 | ||
153 | * open("/sys/dev/block/7:0/ro", O_RDONLY|O_CLOEXEC) = 5 | ||
154 | * read(5, "0\n", 4096) = 2 | ||
155 | * open("/sys/dev/block/7:0/loop/backing_file", O_RDONLY|O_CLOEXEC) = 5 | ||
156 | * read(5, "/PATH/TO/FILE", 4096) = 37 | ||
157 | * open("/sys/dev/block/7:0/loop/dio", O_RDONLY|O_CLOEXEC) = 5 | ||
158 | * read(5, "0\n", 4096) = 2 | ||
159 | */ | ||
160 | |||
136 | bb_show_usage(); /* does not return */ | 161 | bb_show_usage(); /* does not return */ |
137 | /*return EXIT_FAILURE;*/ | 162 | /*return EXIT_FAILURE;*/ |
138 | } | 163 | } |
diff --git a/util-linux/mesg.c b/util-linux/mesg.c new file mode 100644 index 000000000..45c13b8e0 --- /dev/null +++ b/util-linux/mesg.c | |||
@@ -0,0 +1,76 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * mesg implementation for busybox | ||
4 | * | ||
5 | * Copyright (c) 2002 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
8 | */ | ||
9 | |||
10 | //config:config MESG | ||
11 | //config: bool "mesg" | ||
12 | //config: default y | ||
13 | //config: help | ||
14 | //config: Mesg controls access to your terminal by others. It is typically | ||
15 | //config: used to allow or disallow other users to write to your terminal | ||
16 | //config: | ||
17 | //config:config FEATURE_MESG_ENABLE_ONLY_GROUP | ||
18 | //config: bool "Enable writing to tty only by group, not by everybody" | ||
19 | //config: default y | ||
20 | //config: depends on MESG | ||
21 | //config: help | ||
22 | //config: Usually, ttys are owned by group "tty", and "write" tool is | ||
23 | //config: setgid to this group. This way, "mesg y" only needs to enable | ||
24 | //config: "write by owning group" bit in tty mode. | ||
25 | //config: | ||
26 | //config: If you set this option to N, "mesg y" will enable writing | ||
27 | //config: by anybody at all. This is not recommended. | ||
28 | |||
29 | //applet:IF_MESG(APPLET(mesg, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
30 | |||
31 | //kbuild:lib-$(CONFIG_MESG) += mesg.o | ||
32 | |||
33 | //usage:#define mesg_trivial_usage | ||
34 | //usage: "[y|n]" | ||
35 | //usage:#define mesg_full_usage "\n\n" | ||
36 | //usage: "Control write access to your terminal\n" | ||
37 | //usage: " y Allow write access to your terminal\n" | ||
38 | //usage: " n Disallow write access to your terminal" | ||
39 | |||
40 | #include "libbb.h" | ||
41 | |||
42 | #if ENABLE_FEATURE_MESG_ENABLE_ONLY_GROUP | ||
43 | #define S_IWGRP_OR_S_IWOTH S_IWGRP | ||
44 | #else | ||
45 | #define S_IWGRP_OR_S_IWOTH (S_IWGRP | S_IWOTH) | ||
46 | #endif | ||
47 | |||
48 | int mesg_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
49 | int mesg_main(int argc UNUSED_PARAM, char **argv) | ||
50 | { | ||
51 | struct stat sb; | ||
52 | mode_t m; | ||
53 | char c = 0; | ||
54 | |||
55 | argv++; | ||
56 | |||
57 | if (argv[0] | ||
58 | && (argv[1] || ((c = argv[0][0]) != 'y' && c != 'n')) | ||
59 | ) { | ||
60 | bb_show_usage(); | ||
61 | } | ||
62 | |||
63 | if (!isatty(STDIN_FILENO)) | ||
64 | bb_error_msg_and_die("not a tty"); | ||
65 | |||
66 | xfstat(STDIN_FILENO, &sb, "stderr"); | ||
67 | if (c == 0) { | ||
68 | puts((sb.st_mode & (S_IWGRP|S_IWOTH)) ? "is y" : "is n"); | ||
69 | return EXIT_SUCCESS; | ||
70 | } | ||
71 | m = (c == 'y') ? sb.st_mode | S_IWGRP_OR_S_IWOTH | ||
72 | : sb.st_mode & ~(S_IWGRP|S_IWOTH); | ||
73 | if (fchmod(STDIN_FILENO, m) != 0) | ||
74 | bb_perror_nomsg_and_die(); | ||
75 | return EXIT_SUCCESS; | ||
76 | } | ||
diff --git a/util-linux/mount.c b/util-linux/mount.c index f0245f714..6bb18524d 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c | |||
@@ -1887,6 +1887,7 @@ static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts) | |||
1887 | // NB: mp->xxx fields may be trashed on exit | 1887 | // NB: mp->xxx fields may be trashed on exit |
1888 | static int singlemount(struct mntent *mp, int ignore_busy) | 1888 | static int singlemount(struct mntent *mp, int ignore_busy) |
1889 | { | 1889 | { |
1890 | int loopfd = -1; | ||
1890 | int rc = -1; | 1891 | int rc = -1; |
1891 | unsigned long vfsflags; | 1892 | unsigned long vfsflags; |
1892 | char *loopFile = NULL, *filteropts = NULL; | 1893 | char *loopFile = NULL, *filteropts = NULL; |
@@ -2026,7 +2027,20 @@ static int singlemount(struct mntent *mp, int ignore_busy) | |||
2026 | if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) { | 2027 | if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) { |
2027 | loopFile = bb_simplify_path(mp->mnt_fsname); | 2028 | loopFile = bb_simplify_path(mp->mnt_fsname); |
2028 | mp->mnt_fsname = NULL; // will receive malloced loop dev name | 2029 | mp->mnt_fsname = NULL; // will receive malloced loop dev name |
2029 | if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) { | 2030 | |
2031 | // mount always creates AUTOCLEARed loopdevs, so that umounting | ||
2032 | // drops them without any code in the userspace. | ||
2033 | // This happens since circa linux-2.6.25: | ||
2034 | // commit 96c5865559cee0f9cbc5173f3c949f6ce3525581 | ||
2035 | // Date: Wed Feb 6 01:36:27 2008 -0800 | ||
2036 | // Subject: Allow auto-destruction of loop devices | ||
2037 | loopfd = set_loop(&mp->mnt_fsname, | ||
2038 | loopFile, | ||
2039 | 0, | ||
2040 | ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0) | ||
2041 | | BB_LO_FLAGS_AUTOCLEAR | ||
2042 | ); | ||
2043 | if (loopfd < 0) { | ||
2030 | if (errno == EPERM || errno == EACCES) | 2044 | if (errno == EPERM || errno == EACCES) |
2031 | bb_error_msg(bb_msg_perm_denied_are_you_root); | 2045 | bb_error_msg(bb_msg_perm_denied_are_you_root); |
2032 | else | 2046 | else |
@@ -2074,6 +2088,8 @@ static int singlemount(struct mntent *mp, int ignore_busy) | |||
2074 | } | 2088 | } |
2075 | 2089 | ||
2076 | // If mount failed, clean up loop file (if any). | 2090 | // If mount failed, clean up loop file (if any). |
2091 | // (Newer kernels which support LO_FLAGS_AUTOCLEAR should not need this, | ||
2092 | // merely "close(loopfd)" should do it?) | ||
2077 | if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) { | 2093 | if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) { |
2078 | del_loop(mp->mnt_fsname); | 2094 | del_loop(mp->mnt_fsname); |
2079 | if (ENABLE_FEATURE_CLEAN_UP) { | 2095 | if (ENABLE_FEATURE_CLEAN_UP) { |
@@ -2086,6 +2102,9 @@ static int singlemount(struct mntent *mp, int ignore_busy) | |||
2086 | if (ENABLE_FEATURE_CLEAN_UP) | 2102 | if (ENABLE_FEATURE_CLEAN_UP) |
2087 | free(filteropts); | 2103 | free(filteropts); |
2088 | 2104 | ||
2105 | if (loopfd >= 0) | ||
2106 | close(loopfd); | ||
2107 | |||
2089 | if (errno == EBUSY && ignore_busy) | 2108 | if (errno == EBUSY && ignore_busy) |
2090 | return 0; | 2109 | return 0; |
2091 | if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL)) | 2110 | if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL)) |
diff --git a/util-linux/mountpoint.c b/util-linux/mountpoint.c new file mode 100644 index 000000000..8b9e1d779 --- /dev/null +++ b/util-linux/mountpoint.c | |||
@@ -0,0 +1,105 @@ | |||
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/util-linux/renice.c b/util-linux/renice.c new file mode 100644 index 000000000..4da3394a8 --- /dev/null +++ b/util-linux/renice.c | |||
@@ -0,0 +1,148 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * renice implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2005 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
8 | */ | ||
9 | |||
10 | /* Notes: | ||
11 | * Setting an absolute priority was obsoleted in SUSv2 and removed | ||
12 | * in SUSv3. However, the common linux version of renice does | ||
13 | * absolute and not relative. So we'll continue supporting absolute, | ||
14 | * although the stdout logging has been removed since both SUSv2 and | ||
15 | * SUSv3 specify that stdout isn't used. | ||
16 | * | ||
17 | * This version is lenient in that it doesn't require any IDs. The | ||
18 | * options -p, -g, and -u are treated as mode switches for the | ||
19 | * following IDs (if any). Multiple switches are allowed. | ||
20 | */ | ||
21 | //config:config RENICE | ||
22 | //config: bool "renice" | ||
23 | //config: default y | ||
24 | //config: help | ||
25 | //config: Renice alters the scheduling priority of one or more running | ||
26 | //config: processes. | ||
27 | |||
28 | //applet:IF_RENICE(APPLET(renice, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
29 | |||
30 | //kbuild:lib-$(CONFIG_RENICE) += renice.o | ||
31 | |||
32 | //usage:#define renice_trivial_usage | ||
33 | //usage: "[-n] PRIORITY [[-p | -g | -u] ID...]..." | ||
34 | //usage:#define renice_full_usage "\n\n" | ||
35 | //usage: "Change scheduling priority of a running process\n" | ||
36 | //usage: "\n -n Add PRIORITY to current nice value" | ||
37 | //usage: "\n Without -n, nice value is set to PRIORITY" | ||
38 | //usage: "\n -p Process ids (default)" | ||
39 | //usage: "\n -g Process group ids" | ||
40 | //usage: "\n -u Process user names" | ||
41 | |||
42 | #include "libbb.h" | ||
43 | #include <sys/resource.h> | ||
44 | |||
45 | void BUG_bad_PRIO_PROCESS(void); | ||
46 | void BUG_bad_PRIO_PGRP(void); | ||
47 | void BUG_bad_PRIO_USER(void); | ||
48 | |||
49 | int renice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
50 | int renice_main(int argc UNUSED_PARAM, char **argv) | ||
51 | { | ||
52 | static const char Xetpriority_msg[] ALIGN1 = "%cetpriority"; | ||
53 | |||
54 | int retval = EXIT_SUCCESS; | ||
55 | int which = PRIO_PROCESS; /* Default 'which' value. */ | ||
56 | int use_relative = 0; | ||
57 | int adjustment, new_priority; | ||
58 | unsigned who; | ||
59 | char *arg; | ||
60 | |||
61 | /* Yes, they are not #defines in glibc 2.4! #if won't work */ | ||
62 | if (PRIO_PROCESS < CHAR_MIN || PRIO_PROCESS > CHAR_MAX) | ||
63 | BUG_bad_PRIO_PROCESS(); | ||
64 | if (PRIO_PGRP < CHAR_MIN || PRIO_PGRP > CHAR_MAX) | ||
65 | BUG_bad_PRIO_PGRP(); | ||
66 | if (PRIO_USER < CHAR_MIN || PRIO_USER > CHAR_MAX) | ||
67 | BUG_bad_PRIO_USER(); | ||
68 | |||
69 | arg = *++argv; | ||
70 | |||
71 | /* Check if we are using a relative adjustment. */ | ||
72 | if (arg && arg[0] == '-' && arg[1] == 'n') { | ||
73 | use_relative = 1; | ||
74 | if (!arg[2]) | ||
75 | arg = *++argv; | ||
76 | else | ||
77 | arg += 2; | ||
78 | } | ||
79 | |||
80 | if (!arg) { /* No args? Then show usage. */ | ||
81 | bb_show_usage(); | ||
82 | } | ||
83 | |||
84 | /* Get the priority adjustment (absolute or relative). */ | ||
85 | adjustment = xatoi_range(arg, INT_MIN/2, INT_MAX/2); | ||
86 | |||
87 | while ((arg = *++argv) != NULL) { | ||
88 | /* Check for a mode switch. */ | ||
89 | if (arg[0] == '-' && arg[1]) { | ||
90 | static const char opts[] ALIGN1 = { | ||
91 | 'p', 'g', 'u', 0, PRIO_PROCESS, PRIO_PGRP, PRIO_USER | ||
92 | }; | ||
93 | const char *p = strchr(opts, arg[1]); | ||
94 | if (p) { | ||
95 | which = p[4]; | ||
96 | if (!arg[2]) | ||
97 | continue; | ||
98 | arg += 2; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | /* Process an ID arg. */ | ||
103 | if (which == PRIO_USER) { | ||
104 | struct passwd *p; | ||
105 | p = getpwnam(arg); | ||
106 | if (!p) { | ||
107 | bb_error_msg("unknown user %s", arg); | ||
108 | goto HAD_ERROR; | ||
109 | } | ||
110 | who = p->pw_uid; | ||
111 | } else { | ||
112 | who = bb_strtou(arg, NULL, 10); | ||
113 | if (errno) { | ||
114 | bb_error_msg("invalid number '%s'", arg); | ||
115 | goto HAD_ERROR; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | /* Get priority to use, and set it. */ | ||
120 | if (use_relative) { | ||
121 | int old_priority; | ||
122 | |||
123 | errno = 0; /* Needed for getpriority error detection. */ | ||
124 | old_priority = getpriority(which, who); | ||
125 | if (errno) { | ||
126 | bb_perror_msg(Xetpriority_msg, 'g'); | ||
127 | goto HAD_ERROR; | ||
128 | } | ||
129 | |||
130 | new_priority = old_priority + adjustment; | ||
131 | } else { | ||
132 | new_priority = adjustment; | ||
133 | } | ||
134 | |||
135 | if (setpriority(which, who, new_priority) == 0) { | ||
136 | continue; | ||
137 | } | ||
138 | |||
139 | bb_perror_msg(Xetpriority_msg, 's'); | ||
140 | HAD_ERROR: | ||
141 | retval = EXIT_FAILURE; | ||
142 | } | ||
143 | |||
144 | /* No need to check for errors outputting to stderr since, if it | ||
145 | * was used, the HAD_ERROR label was reached and retval was set. */ | ||
146 | |||
147 | return retval; | ||
148 | } | ||
diff --git a/util-linux/setsid.c b/util-linux/setsid.c new file mode 100644 index 000000000..143a8f8fa --- /dev/null +++ b/util-linux/setsid.c | |||
@@ -0,0 +1,82 @@ | |||
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/util-linux/switch_root.c b/util-linux/switch_root.c index 6034485d7..f18e8a5ce 100644 --- a/util-linux/switch_root.c +++ b/util-linux/switch_root.c | |||
@@ -141,10 +141,12 @@ int switch_root_main(int argc UNUSED_PARAM, char **argv) | |||
141 | 141 | ||
142 | // If a new console specified, redirect stdin/stdout/stderr to it | 142 | // If a new console specified, redirect stdin/stdout/stderr to it |
143 | if (console) { | 143 | if (console) { |
144 | close(0); | 144 | int fd = open_or_warn(console, O_RDWR); |
145 | xopen(console, O_RDWR); | 145 | if (fd >= 0) { |
146 | xdup2(0, 1); | 146 | xmove_fd(fd, 0); |
147 | xdup2(0, 2); | 147 | xdup2(0, 1); |
148 | xdup2(0, 2); | ||
149 | } | ||
148 | } | 150 | } |
149 | 151 | ||
150 | // Exec real init | 152 | // Exec real init |
@@ -181,7 +183,7 @@ So there's a step that needs to be sort of atomic but can't be as a shell | |||
181 | script. (You can work around this with static linking or very carefully laid | 183 | script. (You can work around this with static linking or very carefully laid |
182 | out paths and sequencing, but it's brittle, ugly, and non-obvious.) | 184 | out paths and sequencing, but it's brittle, ugly, and non-obvious.) |
183 | 185 | ||
184 | 2) The "find | rm" bit will acually delete everything because the mount points | 186 | 2) The "find | rm" bit will actually delete everything because the mount points |
185 | still show up (even if their contents don't), and rm -rf will then happily zap | 187 | still show up (even if their contents don't), and rm -rf will then happily zap |
186 | that. So the first line is an oversimplification of what you need to do _not_ | 188 | that. So the first line is an oversimplification of what you need to do _not_ |
187 | to descend into other filesystems and delete their contents. | 189 | to descend into other filesystems and delete their contents. |
diff --git a/util-linux/taskset.c b/util-linux/taskset.c new file mode 100644 index 000000000..94a07383a --- /dev/null +++ b/util-linux/taskset.c | |||
@@ -0,0 +1,221 @@ | |||
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/util-linux/umount.c b/util-linux/umount.c index c958fd552..0c50dc9ee 100644 --- a/util-linux/umount.c +++ b/util-linux/umount.c | |||
@@ -42,7 +42,7 @@ | |||
42 | //usage: "\n -l Lazy umount (detach filesystem)" | 42 | //usage: "\n -l Lazy umount (detach filesystem)" |
43 | //usage: "\n -f Force umount (i.e., unreachable NFS server)" | 43 | //usage: "\n -f Force umount (i.e., unreachable NFS server)" |
44 | //usage: IF_FEATURE_MOUNT_LOOP( | 44 | //usage: IF_FEATURE_MOUNT_LOOP( |
45 | //usage: "\n -D Don't free loop device even if it has been used" | 45 | //usage: "\n -d Free loop device if it has been used" |
46 | //usage: ) | 46 | //usage: ) |
47 | //usage: | 47 | //usage: |
48 | //usage:#define umount_example_usage | 48 | //usage:#define umount_example_usage |
@@ -68,22 +68,14 @@ static struct mntent *getmntent_r(FILE* stream, struct mntent* result, | |||
68 | } | 68 | } |
69 | #endif | 69 | #endif |
70 | 70 | ||
71 | /* Ignored: -v -t -i | 71 | /* ignored: -v -t -i */ |
72 | * bbox always acts as if -d is present. | 72 | #define OPTION_STRING "fldnra" "vt:i" |
73 | * -D can be used to suppress it (bbox extension). | ||
74 | * Rationale: | ||
75 | * (1) util-linux's umount does it if "loop=..." is seen in /etc/mtab: | ||
76 | * thus, on many systems, bare umount _does_ drop loop devices. | ||
77 | * (2) many users request this feature. | ||
78 | */ | ||
79 | #define OPTION_STRING "fldDnra" "vt:i" | ||
80 | #define OPT_FORCE (1 << 0) // Same as MNT_FORCE | 73 | #define OPT_FORCE (1 << 0) // Same as MNT_FORCE |
81 | #define OPT_LAZY (1 << 1) // Same as MNT_DETACH | 74 | #define OPT_LAZY (1 << 1) // Same as MNT_DETACH |
82 | //#define OPT_FREE_LOOP (1 << 2) // -d is assumed always present | 75 | #define OPT_FREELOOP (1 << 2) |
83 | #define OPT_DONT_FREE_LOOP (1 << 3) | 76 | #define OPT_NO_MTAB (1 << 3) |
84 | #define OPT_NO_MTAB (1 << 4) | 77 | #define OPT_REMOUNT (1 << 4) |
85 | #define OPT_REMOUNT (1 << 5) | 78 | #define OPT_ALL (ENABLE_FEATURE_UMOUNT_ALL ? (1 << 5) : 0) |
86 | #define OPT_ALL (ENABLE_FEATURE_UMOUNT_ALL ? (1 << 6) : 0) | ||
87 | 79 | ||
88 | int umount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 80 | int umount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
89 | int umount_main(int argc UNUSED_PARAM, char **argv) | 81 | int umount_main(int argc UNUSED_PARAM, char **argv) |
@@ -206,7 +198,7 @@ int umount_main(int argc UNUSED_PARAM, char **argv) | |||
206 | } else { | 198 | } else { |
207 | // De-allocate the loop device. This ioctl should be ignored on | 199 | // De-allocate the loop device. This ioctl should be ignored on |
208 | // any non-loop block devices. | 200 | // any non-loop block devices. |
209 | if (ENABLE_FEATURE_MOUNT_LOOP && !(opt & OPT_DONT_FREE_LOOP) && m) | 201 | if (ENABLE_FEATURE_MOUNT_LOOP && (opt & OPT_FREELOOP) && m) |
210 | del_loop(m->device); | 202 | del_loop(m->device); |
211 | if (ENABLE_FEATURE_MTAB_SUPPORT && !(opt & OPT_NO_MTAB) && m) | 203 | if (ENABLE_FEATURE_MTAB_SUPPORT && !(opt & OPT_NO_MTAB) && m) |
212 | erase_mtab(m->dir); | 204 | erase_mtab(m->dir); |
diff --git a/util-linux/volume_id/udf.c b/util-linux/volume_id/udf.c index 613c80c86..fa5dccee7 100644 --- a/util-linux/volume_id/udf.c +++ b/util-linux/volume_id/udf.c | |||
@@ -137,7 +137,7 @@ anchor: | |||
137 | if (type != 2) /* TAG_ID_AVDP */ | 137 | if (type != 2) /* TAG_ID_AVDP */ |
138 | goto found; | 138 | goto found; |
139 | 139 | ||
140 | /* get desriptor list address and block count */ | 140 | /* get descriptor list address and block count */ |
141 | count = le32_to_cpu(vd->type.anchor.length) / bs; | 141 | count = le32_to_cpu(vd->type.anchor.length) / bs; |
142 | loc = le32_to_cpu(vd->type.anchor.location); | 142 | loc = le32_to_cpu(vd->type.anchor.location); |
143 | dbg("0x%x descriptors starting at logical secor 0x%x", count, loc); | 143 | dbg("0x%x descriptors starting at logical secor 0x%x", count, loc); |
diff --git a/util-linux/wall.c b/util-linux/wall.c new file mode 100644 index 000000000..50658f457 --- /dev/null +++ b/util-linux/wall.c | |||
@@ -0,0 +1,63 @@ | |||
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 | } | ||