1 /* Copyright (C) 2014-2018 Free Software Foundation, Inc.
2 
3    This program is free software: you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation, either version 3 of the License, or
6    (at your option) any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
15 
16 /* coreutils.c aggregates the functionality of every other tool into a single
17    binary multiplexed by the value of argv[0]. This is enabled by passing
18    --enable-single-binary to configure.
19 
20    Written by Alex Deymo <deymo@chromium.org>.  */
21 
22 #include <config.h>
23 #include <getopt.h>
24 #include <stdio.h>
25 #if HAVE_PRCTL
26 # include <sys/prctl.h>
27 #endif
28 
29 #include "system.h"
30 #include "die.h"
31 #include "error.h"
32 #include "quote.h"
33 
34 #ifdef SINGLE_BINARY
35 /* Declare the main function on each one of the selected tools.  This name
36    needs to match the one passed as CFLAGS on single-binary.mk (generated
37    by gen-single-binary.sh). */
38 # define SINGLE_BINARY_PROGRAM(prog_name_str, main_name) \
39   int single_binary_main_##main_name (int, char **);
40 # include "coreutils.h"
41 # undef SINGLE_BINARY_PROGRAM
42 #endif
43 
44 /* The official name of this program (e.g., no 'g' prefix).  */
45 #define PROGRAM_NAME "coreutils"
46 
47 #define AUTHORS \
48   proper_name ("Alex Deymo")
49 
50 static struct option const long_options[] =
51 {
52   {GETOPT_HELP_OPTION_DECL},
53   {GETOPT_VERSION_OPTION_DECL},
54   {NULL, 0, NULL, 0}
55 };
56 
57 
58 void
usage(int status)59 usage (int status)
60 {
61   if (status != EXIT_SUCCESS)
62     emit_try_help ();
63   else
64     {
65       printf (_("\
66 Usage: %s --coreutils-prog=PROGRAM_NAME [PARAMETERS]... \n"),
67               program_name);
68       fputs (_("\
69 Execute the PROGRAM_NAME built-in program with the given PARAMETERS.\n\
70 \n"), stdout);
71       fputs (HELP_OPTION_DESCRIPTION, stdout);
72       fputs (VERSION_OPTION_DESCRIPTION, stdout);
73 
74 #ifdef SINGLE_BINARY
75 /* XXX: Ideally we'd like to present "install" here, not "ginstall".  */
76       char const *prog_name_list =
77 # define SINGLE_BINARY_PROGRAM(prog_name_str, main_name) " " prog_name_str
78 # include "coreutils.h"
79 # undef SINGLE_BINARY_PROGRAM
80       ;
81       printf ("\n\
82 Built-in programs:\n\
83 %s\n", prog_name_list);
84 #endif
85 
86       printf (_("\
87 \n\
88 Use: '%s --coreutils-prog=PROGRAM_NAME --help' for individual program help.\n"),
89               program_name);
90       emit_ancillary_info (PROGRAM_NAME);
91     }
92   exit (status);
93 }
94 
95 static void
launch_program(const char * prog_name,int prog_argc,char ** prog_argv)96 launch_program (const char *prog_name, int prog_argc, char **prog_argv)
97 {
98   int (*prog_main) (int, char **) = NULL;
99 
100   /* Ensure that at least one parameter was passed.  */
101   if (!prog_argc || !prog_argv || !prog_argv[0] || !prog_name)
102     return;
103 
104 #ifdef SINGLE_BINARY
105   if (false);
106   /* Look up the right main program.  */
107 # define SINGLE_BINARY_PROGRAM(prog_name_str, main_name) \
108   else if (STREQ (prog_name_str, prog_name)) \
109     prog_main = single_binary_main_##main_name;
110 # include "coreutils.h"
111 # undef SINGLE_BINARY_PROGRAM
112 #endif
113 
114   if (! prog_main)
115     return;
116 
117 #if HAVE_PRCTL && defined PR_SET_NAME
118   /* Not being able to set the program name is not a fatal error.  */
119   prctl (PR_SET_NAME, prog_argv[0]);
120 #endif
121 #if HAVE_PRCTL && defined PR_SET_MM_ARG_START
122   /* Shift the beginning of the command line to prog_argv[0] (if set) so
123      /proc/pid/cmdline reflects the right value.  */
124   prctl (PR_SET_MM_ARG_START, prog_argv[0]);
125 #endif
126 
127   exit (prog_main (prog_argc, prog_argv));
128 }
129 
130 int
main(int argc,char ** argv)131 main (int argc, char **argv)
132 {
133   char *prog_name = last_component (argv[0]);
134   int optc;
135 
136   /* Map external name to internal name.  */
137   char ginstall[] = "ginstall";
138   if (STREQ (prog_name, "install"))
139     prog_name = ginstall;
140 
141   /* If this program is called directly as "coreutils" or if the value of
142      argv[0] is an unknown tool (which "coreutils" is), we proceed and parse
143      the options.  */
144   launch_program (prog_name, argc, argv);
145 
146   /* No known program was selected via argv[0].  Try parsing the first
147      argument as --coreutils-prog=PROGRAM to determine the program.  The
148      invocation for this case should be:
149        path/to/coreutils --coreutils-prog=someprog someprog ...
150      The third argument is what the program will see as argv[0].  */
151 
152   if (argc >= 2)
153     {
154       size_t nskip = 0;
155       char *arg_name = NULL;
156 
157       /* If calling coreutils directly, the "script" name isn't passed.
158          Distinguish the two cases with a -shebang suffix.  */
159       if (STRPREFIX (argv[1], "--coreutils-prog="))
160         {
161           nskip = 1;
162           arg_name = prog_name = argv[1] + strlen ("--coreutils-prog=");
163         }
164       else if (STRPREFIX (argv[1], "--coreutils-prog-shebang="))
165         {
166           nskip = 2;
167           prog_name = argv[1] + strlen ("--coreutils-prog-shebang=");
168           if (argc >= 3)
169             arg_name = last_component (argv[2]);
170           else
171             arg_name = prog_name;
172         }
173 
174       if (nskip)
175         {
176           argv[nskip] = arg_name; /* XXX: Discards any specified path.  */
177           launch_program (prog_name, argc - nskip, argv + nskip);
178           die (EXIT_FAILURE, 0, _("unknown program %s"),
179                quote (prog_name));
180         }
181     }
182 
183   /* No known program was selected.  From here on, we behave like any other
184      coreutils program.  */
185   initialize_main (&argc, &argv);
186   set_program_name (argv[0]);
187   setlocale (LC_ALL, "");
188   bindtextdomain (PACKAGE, LOCALEDIR);
189   textdomain (PACKAGE);
190   atexit (close_stdout);
191 
192   if ((optc = getopt_long (argc, argv, "", long_options, NULL)) != -1)
193     switch (optc)
194       {
195       case_GETOPT_HELP_CHAR;
196 
197       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
198       }
199 
200   /* Only print the error message when no options have been passed
201      to coreutils.  */
202   if (optind == 1 && prog_name && !STREQ (prog_name, "coreutils"))
203     error (0, 0, _("unknown program %s"),
204            quote (prog_name));
205 
206   usage (EXIT_FAILURE);
207 }
208