1 /* env - run a program in a modified environment
2 Copyright (C) 1986-2023 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17 /* Richard Mlynarik and David MacKenzie */
18
19 #include <config.h>
20 #include <stdio.h>
21 #include <sys/types.h>
22 #include <getopt.h>
23 #include <c-ctype.h>
24 #include <signal.h>
25
26 #include "system.h"
27 #include "operand2sig.h"
28 #include "quote.h"
29 #include "sig2str.h"
30
31 /* The official name of this program (e.g., no 'g' prefix). */
32 #define PROGRAM_NAME "env"
33
34 #define AUTHORS \
35 proper_name ("Richard Mlynarik"), \
36 proper_name ("David MacKenzie"), \
37 proper_name ("Assaf Gordon")
38
39 /* Array of envvars to unset. */
40 static char const **usvars;
41 static size_t usvars_alloc;
42 static idx_t usvars_used;
43
44 /* Annotate the output with extra info to aid the user. */
45 static bool dev_debug;
46
47 /* Buffer and length of extracted envvars in -S strings. */
48 static char *varname;
49 static idx_t vnlen;
50
51 /* Possible actions on each signal. */
52 enum SIGNAL_MODE {
53 UNCHANGED = 0,
54 DEFAULT, /* Set to default handler (SIG_DFL). */
55 DEFAULT_NOERR, /* Ditto, but ignore sigaction(2) errors. */
56 IGNORE, /* Set to ignore (SIG_IGN). */
57 IGNORE_NOERR /* Ditto, but ignore sigaction(2) errors. */
58 };
59 static enum SIGNAL_MODE *signals;
60
61 /* Set of signals to block. */
62 static sigset_t block_signals;
63
64 /* Set of signals to unblock. */
65 static sigset_t unblock_signals;
66
67 /* Whether signal mask adjustment requested. */
68 static bool sig_mask_changed;
69
70 /* Whether to list non default handling. */
71 static bool report_signal_handling;
72
73 /* The isspace characters in the C locale. */
74 #define C_ISSPACE_CHARS " \t\n\v\f\r"
75
76 static char const shortopts[] = "+C:iS:u:v0" C_ISSPACE_CHARS;
77
78 /* For long options that have no equivalent short option, use a
79 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
80 enum
81 {
82 DEFAULT_SIGNAL_OPTION = CHAR_MAX + 1,
83 IGNORE_SIGNAL_OPTION,
84 BLOCK_SIGNAL_OPTION,
85 LIST_SIGNAL_HANDLING_OPTION,
86 };
87
88 static struct option const longopts[] =
89 {
90 {"ignore-environment", no_argument, nullptr, 'i'},
91 {"null", no_argument, nullptr, '0'},
92 {"unset", required_argument, nullptr, 'u'},
93 {"chdir", required_argument, nullptr, 'C'},
94 {"default-signal", optional_argument, nullptr, DEFAULT_SIGNAL_OPTION},
95 {"ignore-signal", optional_argument, nullptr, IGNORE_SIGNAL_OPTION},
96 {"block-signal", optional_argument, nullptr, BLOCK_SIGNAL_OPTION},
97 {"list-signal-handling", no_argument, nullptr, LIST_SIGNAL_HANDLING_OPTION},
98 {"debug", no_argument, nullptr, 'v'},
99 {"split-string", required_argument, nullptr, 'S'},
100 {GETOPT_HELP_OPTION_DECL},
101 {GETOPT_VERSION_OPTION_DECL},
102 {nullptr, 0, nullptr, 0}
103 };
104
105 void
usage(int status)106 usage (int status)
107 {
108 if (status != EXIT_SUCCESS)
109 emit_try_help ();
110 else
111 {
112 printf (_("\
113 Usage: %s [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]\n"),
114 program_name);
115 fputs (_("\
116 Set each NAME to VALUE in the environment and run COMMAND.\n\
117 "), stdout);
118
119 emit_mandatory_arg_note ();
120
121 fputs (_("\
122 -i, --ignore-environment start with an empty environment\n\
123 -0, --null end each output line with NUL, not newline\n\
124 -u, --unset=NAME remove variable from the environment\n\
125 "), stdout);
126 fputs (_("\
127 -C, --chdir=DIR change working directory to DIR\n\
128 "), stdout);
129 fputs (_("\
130 -S, --split-string=S process and split S into separate arguments;\n\
131 used to pass multiple arguments on shebang lines\n\
132 "), stdout);
133 fputs (_("\
134 --block-signal[=SIG] block delivery of SIG signal(s) to COMMAND\n\
135 "), stdout);
136 fputs (_("\
137 --default-signal[=SIG] reset handling of SIG signal(s) to the default\n\
138 "), stdout);
139 fputs (_("\
140 --ignore-signal[=SIG] set handling of SIG signal(s) to do nothing\n\
141 "), stdout);
142 fputs (_("\
143 --list-signal-handling list non default signal handling to stderr\n\
144 "), stdout);
145 fputs (_("\
146 -v, --debug print verbose information for each processing step\n\
147 "), stdout);
148 fputs (HELP_OPTION_DESCRIPTION, stdout);
149 fputs (VERSION_OPTION_DESCRIPTION, stdout);
150 fputs (_("\
151 \n\
152 A mere - implies -i. If no COMMAND, print the resulting environment.\n\
153 "), stdout);
154 fputs (_("\
155 \n\
156 SIG may be a signal name like 'PIPE', or a signal number like '13'.\n\
157 Without SIG, all known signals are included. Multiple signals can be\n\
158 comma-separated. An empty SIG argument is a no-op.\n\
159 "), stdout);
160 emit_exec_status (PROGRAM_NAME);
161 emit_ancillary_info (PROGRAM_NAME);
162 }
163 exit (status);
164 }
165
166 static void
append_unset_var(char const * var)167 append_unset_var (char const *var)
168 {
169 if (usvars_used == usvars_alloc)
170 usvars = x2nrealloc (usvars, &usvars_alloc, sizeof *usvars);
171 usvars[usvars_used++] = var;
172 }
173
174 static void
unset_envvars(void)175 unset_envvars (void)
176 {
177 for (idx_t i = 0; i < usvars_used; ++i)
178 {
179 devmsg ("unset: %s\n", usvars[i]);
180
181 if (unsetenv (usvars[i]))
182 error (EXIT_CANCELED, errno, _("cannot unset %s"),
183 quote (usvars[i]));
184 }
185 }
186
187 /* Return a pointer to the end of a valid ${VARNAME} string, or nullptr.
188 'str' should point to the '$' character.
189 First letter in VARNAME must be alpha or underscore,
190 rest of letters are alnum or underscore.
191 Any other character is an error. */
192 ATTRIBUTE_PURE
193 static char const *
scan_varname(char const * str)194 scan_varname (char const *str)
195 {
196 if (str[1] == '{' && (c_isalpha (str[2]) || str[2] == '_'))
197 {
198 char const *end = str + 3;
199 while (c_isalnum (*end) || *end == '_')
200 ++end;
201 if (*end == '}')
202 return end;
203 }
204
205 return nullptr;
206 }
207
208 /* Return a pointer to a static buffer containing the VARNAME as
209 extracted from a '${VARNAME}' string.
210 The returned string will be NUL terminated.
211 The returned pointer should not be freed.
212 Return nullptr if not a valid ${VARNAME} syntax. */
213 static char *
extract_varname(char const * str)214 extract_varname (char const *str)
215 {
216 idx_t i;
217 char const *p;
218
219 p = scan_varname (str);
220 if (!p)
221 return nullptr;
222
223 /* -2 and +2 (below) account for the '${' prefix. */
224 i = p - str - 2;
225
226 if (i >= vnlen)
227 {
228 vnlen = i + 1;
229 varname = xrealloc (varname, vnlen);
230 }
231
232 memcpy (varname, str + 2, i);
233 varname[i] = 0;
234
235 return varname;
236 }
237
238 /* Temporary buffer used by --split-string processing. */
239 struct splitbuf
240 {
241 /* Buffer address, arg count, and half the number of elements in the buffer.
242 ARGC and ARGV are as in 'main', and ARGC + 1 <= HALF_ALLOC so
243 that the upper half of ARGV can be used for string contents.
244 This may waste up to half the space but keeps the code simple,
245 which is better for this rarely-used but security-sensitive code.
246
247 ARGV[0] is not initialized; that is the caller's responsibility
248 after finalization.
249
250 During assembly, ARGV[I] (where 0 < I < ARGC) contains the offset
251 of the Ith string (relative to ARGV + HALF_ALLOC), so that
252 reallocating ARGV does not change the validity of its contents.
253 The integer offset is cast to char * during assembly, and is
254 converted to a true char * pointer on finalization.
255
256 During assembly, ARGV[ARGC] contains the offset of the first
257 unused string byte (relative to ARGV + HALF_ALLOC). */
258 char **argv;
259 int argc;
260 idx_t half_alloc;
261
262 /* The number of extra argv slots to keep room for. */
263 int extra_argc;
264
265 /* Whether processing should act as if the most recent character
266 seen was a separator. */
267 bool sep;
268 };
269
270 /* Expand SS so that it has at least one more argv slot and at least
271 one more string byte. */
272 static void
splitbuf_grow(struct splitbuf * ss)273 splitbuf_grow (struct splitbuf *ss)
274 {
275 idx_t old_half_alloc = ss->half_alloc;
276 idx_t string_bytes = (intptr_t) ss->argv[ss->argc];
277 ss->argv = xpalloc (ss->argv, &ss->half_alloc, 1,
278 MIN (INT_MAX, IDX_MAX), 2 * sizeof *ss->argv);
279 memmove (ss->argv + ss->half_alloc, ss->argv + old_half_alloc, string_bytes);
280 }
281
282 /* In SS, append C to the last string. */
283 static void
splitbuf_append_byte(struct splitbuf * ss,char c)284 splitbuf_append_byte (struct splitbuf *ss, char c)
285 {
286 idx_t string_bytes = (intptr_t) ss->argv[ss->argc];
287 if (ss->half_alloc * sizeof *ss->argv <= string_bytes)
288 splitbuf_grow (ss);
289 ((char *) (ss->argv + ss->half_alloc))[string_bytes] = c;
290 ss->argv[ss->argc] = (char *) (intptr_t) (string_bytes + 1);
291 }
292
293 /* If SS's most recent character was a separator, finish off its
294 previous argument and start a new one. */
295 static void
check_start_new_arg(struct splitbuf * ss)296 check_start_new_arg (struct splitbuf *ss)
297 {
298 if (ss->sep)
299 {
300 splitbuf_append_byte (ss, '\0');
301 int argc = ss->argc;
302 if (ss->half_alloc <= argc + ss->extra_argc + 1)
303 splitbuf_grow (ss);
304 ss->argv[argc + 1] = ss->argv[argc];
305 ss->argc = argc + 1;
306 ss->sep = false;
307 }
308 }
309
310 /* All additions to SS have been made. Convert its offsets to pointers,
311 and return the resulting argument vector. */
312 static char **
splitbuf_finishup(struct splitbuf * ss)313 splitbuf_finishup (struct splitbuf *ss)
314 {
315 int argc = ss->argc;
316 char **argv = ss->argv;
317 char *stringbase = (char *) (ss->argv + ss->half_alloc);
318 for (int i = 1; i < argc; i++)
319 argv[i] = stringbase + (intptr_t) argv[i];
320 return argv;
321 }
322
323 /* Return a newly-allocated argv-like array,
324 by parsing and splitting the input 'str'.
325
326 'extra_argc' is the number of additional elements to allocate
327 in the array (on top of the number of args required to split 'str').
328
329 Store into *argc the number of arguments found (plus 1 for
330 the program name).
331
332 Example:
333 int argc;
334 char **argv = build_argv ("A=B uname -k', 3, &argc);
335 Results in:
336 argc = 4
337 argv[0] = [not initialized]
338 argv[1] = "A=B"
339 argv[2] = "uname"
340 argv[3] = "-k"
341 argv[4,5,6,7] = [allocated due to extra_argc + 1, but not initialized]
342
343 To free allocated memory:
344 free (argv);
345 However, 'env' does not free since it's about to exec or exit anyway
346 and the complexity of keeping track of the storage that may have been
347 allocated via multiple calls to build_argv is not worth the hassle. */
348 static char **
build_argv(char const * str,int extra_argc,int * argc)349 build_argv (char const *str, int extra_argc, int *argc)
350 {
351 bool dq = false, sq = false;
352 struct splitbuf ss;
353 ss.argv = xnmalloc (extra_argc + 2, 2 * sizeof *ss.argv);
354 ss.argc = 1;
355 ss.half_alloc = extra_argc + 2;
356 ss.extra_argc = extra_argc;
357 ss.sep = true;
358 ss.argv[ss.argc] = 0;
359
360 /* In the following loop,
361 'break' causes the character 'newc' to be added to *dest,
362 'continue' skips the character. */
363 while (*str)
364 {
365 char newc = *str; /* Default: add the next character. */
366
367 switch (*str)
368 {
369 case '\'':
370 if (dq)
371 break;
372 sq = !sq;
373 check_start_new_arg (&ss);
374 ++str;
375 continue;
376
377 case '"':
378 if (sq)
379 break;
380 dq = !dq;
381 check_start_new_arg (&ss);
382 ++str;
383 continue;
384
385 case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
386 /* Start a new argument if outside quotes. */
387 if (sq || dq)
388 break;
389 ss.sep = true;
390 str += strspn (str, C_ISSPACE_CHARS);
391 continue;
392
393 case '#':
394 if (!ss.sep)
395 break;
396 goto eos; /* '#' as first char terminates the string. */
397
398 case '\\':
399 /* Backslash inside single-quotes is not special, except \\
400 and \'. */
401 if (sq && str[1] != '\\' && str[1] != '\'')
402 break;
403
404 /* Skip the backslash and examine the next character. */
405 newc = *++str;
406 switch (newc)
407 {
408 case '"': case '#': case '$': case '\'': case '\\':
409 /* Pass escaped character as-is. */
410 break;
411
412 case '_':
413 if (!dq)
414 {
415 ++str; /* '\_' outside double-quotes is arg separator. */
416 ss.sep = true;
417 continue;
418 }
419 newc = ' '; /* '\_' inside double-quotes is space. */
420 break;
421
422 case 'c':
423 if (dq)
424 error (EXIT_CANCELED, 0,
425 _("'\\c' must not appear in double-quoted -S string"));
426 goto eos; /* '\c' terminates the string. */
427
428 case 'f': newc = '\f'; break;
429 case 'n': newc = '\n'; break;
430 case 'r': newc = '\r'; break;
431 case 't': newc = '\t'; break;
432 case 'v': newc = '\v'; break;
433
434 case '\0':
435 error (EXIT_CANCELED, 0,
436 _("invalid backslash at end of string in -S"));
437
438 default:
439 error (EXIT_CANCELED, 0,
440 _("invalid sequence '\\%c' in -S"), newc);
441 }
442 break;
443
444 case '$':
445 /* ${VARNAME} are not expanded inside single-quotes. */
446 if (sq)
447 break;
448
449 /* Store the ${VARNAME} value. */
450 {
451 char *n = extract_varname (str);
452 if (!n)
453 error (EXIT_CANCELED, 0,
454 _("only ${VARNAME} expansion is supported, error at: %s"),
455 str);
456
457 char *v = getenv (n);
458 if (v)
459 {
460 check_start_new_arg (&ss);
461 devmsg ("expanding ${%s} into %s\n", n, quote (v));
462 for (; *v; v++)
463 splitbuf_append_byte (&ss, *v);
464 }
465 else
466 devmsg ("replacing ${%s} with null string\n", n);
467
468 str = strchr (str, '}') + 1;
469 continue;
470 }
471 }
472
473 check_start_new_arg (&ss);
474 splitbuf_append_byte (&ss, newc);
475 ++str;
476 }
477
478 if (dq || sq)
479 error (EXIT_CANCELED, 0, _("no terminating quote in -S string"));
480
481 eos:
482 splitbuf_append_byte (&ss, '\0');
483 *argc = ss.argc;
484 return splitbuf_finishup (&ss);
485 }
486
487 /* Process an "-S" string and create the corresponding argv array.
488 Update the given argc/argv parameters with the new argv.
489
490 Example: if executed as:
491 $ env -S"-i -C/tmp A=B" foo bar
492 The input argv is:
493 argv[0] = "env"
494 argv[1] = "-S-i -C/tmp A=B"
495 argv[2] = "foo"
496 argv[3] = "bar"
497 argv[4] = nullptr
498 This function will modify argv to be:
499 argv[0] = "env"
500 argv[1] = "-i"
501 argv[2] = "-C/tmp"
502 argv[3] = "A=B"
503 argv[4] = "foo"
504 argv[5] = "bar"
505 argv[6] = nullptr
506 argc will be updated from 4 to 6.
507 optind will be reset to 0 to force getopt_long to rescan all arguments. */
508 static void
parse_split_string(char const * str,int * orig_optind,int * orig_argc,char *** orig_argv)509 parse_split_string (char const *str, int *orig_optind,
510 int *orig_argc, char ***orig_argv)
511 {
512 int extra_argc = *orig_argc - *orig_optind, newargc;
513 char **newargv = build_argv (str, extra_argc, &newargc);
514
515 /* Restore argv[0] - the 'env' executable name. */
516 *newargv = (*orig_argv)[0];
517
518 /* Print parsed arguments. */
519 if (dev_debug && 1 < newargc)
520 {
521 devmsg ("split -S: %s\n", quote (str));
522 devmsg (" into: %s\n", quote (newargv[1]));
523 for (int i = 2; i < newargc; i++)
524 devmsg (" & %s\n", quote (newargv[i]));
525 }
526
527 /* Add remaining arguments and terminating null from the original
528 command line. */
529 memcpy (newargv + newargc, *orig_argv + *orig_optind,
530 (extra_argc + 1) * sizeof *newargv);
531
532 /* Set new values for original getopt variables. */
533 *orig_argc = newargc + extra_argc;
534 *orig_argv = newargv;
535 *orig_optind = 0; /* Tell getopt to restart from first argument. */
536 }
537
538 static void
parse_signal_action_params(char const * optarg,bool set_default)539 parse_signal_action_params (char const *optarg, bool set_default)
540 {
541 char signame[SIG2STR_MAX];
542 char *opt_sig;
543 char *optarg_writable;
544
545 if (! optarg)
546 {
547 /* Without an argument, reset all signals.
548 Some signals cannot be set to ignore or default (e.g., SIGKILL,
549 SIGSTOP on most OSes, and SIGCONT on AIX.) - so ignore errors. */
550 for (int i = 1 ; i <= SIGNUM_BOUND; i++)
551 if (sig2str (i, signame) == 0)
552 signals[i] = set_default ? DEFAULT_NOERR : IGNORE_NOERR;
553 return;
554 }
555
556 optarg_writable = xstrdup (optarg);
557
558 opt_sig = strtok (optarg_writable, ",");
559 while (opt_sig)
560 {
561 int signum = operand2sig (opt_sig, signame);
562 /* operand2sig accepts signal 0 (EXIT) - but we reject it. */
563 if (signum == 0)
564 error (0, 0, _("%s: invalid signal"), quote (opt_sig));
565 if (signum <= 0)
566 usage (exit_failure);
567
568 signals[signum] = set_default ? DEFAULT : IGNORE;
569
570 opt_sig = strtok (nullptr, ",");
571 }
572
573 free (optarg_writable);
574 }
575
576 static void
reset_signal_handlers(void)577 reset_signal_handlers (void)
578 {
579 for (int i = 1; i <= SIGNUM_BOUND; i++)
580 {
581 struct sigaction act;
582
583 if (signals[i] == UNCHANGED)
584 continue;
585
586 bool ignore_errors = (signals[i] == DEFAULT_NOERR
587 || signals[i] == IGNORE_NOERR);
588
589 bool set_to_default = (signals[i] == DEFAULT
590 || signals[i] == DEFAULT_NOERR);
591
592 int sig_err = sigaction (i, nullptr, &act);
593
594 if (sig_err && !ignore_errors)
595 error (EXIT_CANCELED, errno,
596 _("failed to get signal action for signal %d"), i);
597
598 if (! sig_err)
599 {
600 act.sa_handler = set_to_default ? SIG_DFL : SIG_IGN;
601 sig_err = sigaction (i, &act, nullptr);
602 if (sig_err && !ignore_errors)
603 error (EXIT_CANCELED, errno,
604 _("failed to set signal action for signal %d"), i);
605 }
606
607 if (dev_debug)
608 {
609 char signame[SIG2STR_MAX];
610 sig2str (i, signame);
611 devmsg ("Reset signal %s (%d) to %s%s\n",
612 signame, i,
613 set_to_default ? "DEFAULT" : "IGNORE",
614 sig_err ? " (failure ignored)" : "");
615 }
616 }
617 }
618
619
620 static void
parse_block_signal_params(char const * optarg,bool block)621 parse_block_signal_params (char const *optarg, bool block)
622 {
623 char signame[SIG2STR_MAX];
624 char *opt_sig;
625 char *optarg_writable;
626
627 if (! optarg)
628 {
629 /* Without an argument, reset all signals. */
630 sigfillset (block ? &block_signals : &unblock_signals);
631 sigemptyset (block ? &unblock_signals : &block_signals);
632 }
633 else if (! sig_mask_changed)
634 {
635 /* Initialize the sets. */
636 sigemptyset (&block_signals);
637 sigemptyset (&unblock_signals);
638 }
639
640 sig_mask_changed = true;
641
642 if (! optarg)
643 return;
644
645 optarg_writable = xstrdup (optarg);
646
647 opt_sig = strtok (optarg_writable, ",");
648 while (opt_sig)
649 {
650 int signum = operand2sig (opt_sig, signame);
651 /* operand2sig accepts signal 0 (EXIT) - but we reject it. */
652 if (signum == 0)
653 error (0, 0, _("%s: invalid signal"), quote (opt_sig));
654 if (signum <= 0)
655 usage (exit_failure);
656
657 sigaddset (block ? &block_signals : &unblock_signals, signum);
658 sigdelset (block ? &unblock_signals : &block_signals, signum);
659
660 opt_sig = strtok (nullptr, ",");
661 }
662
663 free (optarg_writable);
664 }
665
666 static void
set_signal_proc_mask(void)667 set_signal_proc_mask (void)
668 {
669 /* Get the existing signal mask */
670 sigset_t set;
671 char const *debug_act;
672
673 sigemptyset (&set);
674
675 if (sigprocmask (0, nullptr, &set))
676 error (EXIT_CANCELED, errno, _("failed to get signal process mask"));
677
678 for (int i = 1; i <= SIGNUM_BOUND; i++)
679 {
680 if (sigismember (&block_signals, i))
681 {
682 sigaddset (&set, i);
683 debug_act = "BLOCK";
684 }
685 else if (sigismember (&unblock_signals, i))
686 {
687 sigdelset (&set, i);
688 debug_act = "UNBLOCK";
689 }
690 else
691 {
692 debug_act = nullptr;
693 }
694
695 if (dev_debug && debug_act)
696 {
697 char signame[SIG2STR_MAX];
698 sig2str (i, signame);
699 devmsg ("signal %s (%d) mask set to %s\n",
700 signame, i, debug_act);
701 }
702 }
703
704 if (sigprocmask (SIG_SETMASK, &set, nullptr))
705 error (EXIT_CANCELED, errno, _("failed to set signal process mask"));
706 }
707
708 static void
list_signal_handling(void)709 list_signal_handling (void)
710 {
711 sigset_t set;
712 char signame[SIG2STR_MAX];
713
714 sigemptyset (&set);
715 if (sigprocmask (0, nullptr, &set))
716 error (EXIT_CANCELED, errno, _("failed to get signal process mask"));
717
718 for (int i = 1; i <= SIGNUM_BOUND; i++)
719 {
720 struct sigaction act;
721 if (sigaction (i, nullptr, &act))
722 continue;
723
724 char const *ignored = act.sa_handler == SIG_IGN ? "IGNORE" : "";
725 char const *blocked = sigismember (&set, i) ? "BLOCK" : "";
726 char const *connect = *ignored && *blocked ? "," : "";
727
728 if (! *ignored && ! *blocked)
729 continue;
730
731 sig2str (i, signame);
732 fprintf (stderr, "%-10s (%2d): %s%s%s\n", signame, i,
733 blocked, connect, ignored);
734 }
735 }
736
737 static void
initialize_signals(void)738 initialize_signals (void)
739 {
740 signals = xmalloc ((sizeof *signals) * (SIGNUM_BOUND + 1));
741
742 for (int i = 0 ; i <= SIGNUM_BOUND; i++)
743 signals[i] = UNCHANGED;
744
745 return;
746 }
747
748 int
main(int argc,char ** argv)749 main (int argc, char **argv)
750 {
751 int optc;
752 bool ignore_environment = false;
753 bool opt_nul_terminate_output = false;
754 char const *newdir = nullptr;
755
756 initialize_main (&argc, &argv);
757 set_program_name (argv[0]);
758 setlocale (LC_ALL, "");
759 bindtextdomain (PACKAGE, LOCALEDIR);
760 textdomain (PACKAGE);
761
762 initialize_exit_failure (EXIT_CANCELED);
763 atexit (close_stdout);
764
765 initialize_signals ();
766
767 while ((optc = getopt_long (argc, argv, shortopts, longopts, nullptr)) != -1)
768 {
769 switch (optc)
770 {
771 case 'i':
772 ignore_environment = true;
773 break;
774 case 'u':
775 append_unset_var (optarg);
776 break;
777 case 'v':
778 dev_debug = true;
779 break;
780 case '0':
781 opt_nul_terminate_output = true;
782 break;
783 case DEFAULT_SIGNAL_OPTION:
784 parse_signal_action_params (optarg, true);
785 parse_block_signal_params (optarg, false);
786 break;
787 case IGNORE_SIGNAL_OPTION:
788 parse_signal_action_params (optarg, false);
789 break;
790 case BLOCK_SIGNAL_OPTION:
791 parse_block_signal_params (optarg, true);
792 break;
793 case LIST_SIGNAL_HANDLING_OPTION:
794 report_signal_handling = true;
795 break;
796 case 'C':
797 newdir = optarg;
798 break;
799 case 'S':
800 parse_split_string (optarg, &optind, &argc, &argv);
801 break;
802 case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
803 /* These are undocumented options. Attempt to detect
804 incorrect shebang usage with extraneous space, e.g.:
805 #!/usr/bin/env -i command
806 In which case argv[1] == "-i command". */
807 error (0, 0, _("invalid option -- '%c'"), optc);
808 error (0, 0, _("use -[v]S to pass options in shebang lines"));
809 usage (EXIT_CANCELED);
810
811 case_GETOPT_HELP_CHAR;
812 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
813 default:
814 usage (EXIT_CANCELED);
815 }
816 }
817
818 if (optind < argc && STREQ (argv[optind], "-"))
819 {
820 ignore_environment = true;
821 ++optind;
822 }
823
824 if (ignore_environment)
825 {
826 devmsg ("cleaning environ\n");
827 static char *dummy_environ[] = { nullptr };
828 environ = dummy_environ;
829 }
830 else
831 unset_envvars ();
832
833 char *eq;
834 while (optind < argc && (eq = strchr (argv[optind], '=')))
835 {
836 devmsg ("setenv: %s\n", argv[optind]);
837
838 if (putenv (argv[optind]))
839 {
840 *eq = '\0';
841 error (EXIT_CANCELED, errno, _("cannot set %s"),
842 quote (argv[optind]));
843 }
844 optind++;
845 }
846
847 bool program_specified = optind < argc;
848
849 if (opt_nul_terminate_output && program_specified)
850 {
851 error (0, 0, _("cannot specify --null (-0) with command"));
852 usage (EXIT_CANCELED);
853 }
854
855 if (newdir && ! program_specified)
856 {
857 error (0, 0, _("must specify command with --chdir (-C)"));
858 usage (EXIT_CANCELED);
859 }
860
861 if (! program_specified)
862 {
863 /* Print the environment and exit. */
864 char *const *e = environ;
865 while (*e)
866 printf ("%s%c", *e++, opt_nul_terminate_output ? '\0' : '\n');
867 return EXIT_SUCCESS;
868 }
869
870 reset_signal_handlers ();
871 if (sig_mask_changed)
872 set_signal_proc_mask ();
873
874 if (report_signal_handling)
875 list_signal_handling ();
876
877 if (newdir)
878 {
879 devmsg ("chdir: %s\n", quoteaf (newdir));
880
881 if (chdir (newdir) != 0)
882 error (EXIT_CANCELED, errno, _("cannot change directory to %s"),
883 quoteaf (newdir));
884 }
885
886 if (dev_debug)
887 {
888 devmsg ("executing: %s\n", argv[optind]);
889 for (int i=optind; i<argc; ++i)
890 devmsg (" arg[%d]= %s\n", i-optind, quote (argv[i]));
891 }
892
893 execvp (argv[optind], &argv[optind]);
894
895 int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
896 error (0, errno, "%s", quote (argv[optind]));
897
898 if (exit_status == EXIT_ENOENT && strpbrk (argv[optind], C_ISSPACE_CHARS))
899 error (0, 0, _("use -[v]S to pass options in shebang lines"));
900
901 main_exit (exit_status);
902 }
903