1 /* Copyright (C) 2014-2023 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 "quote.h"
31 
32 #ifdef SINGLE_BINARY
33 /* Declare the main function on each one of the selected tools.  This name
34    needs to match the one passed as CFLAGS on single-binary.mk (generated
35    by gen-single-binary.sh). */
36 # define SINGLE_BINARY_PROGRAM(prog_name_str, main_name) \
37   int single_binary_main_##main_name (int, char **);
38 # include "coreutils.h"
39 # undef SINGLE_BINARY_PROGRAM
40 #endif
41 
42 /* The official name of this program (e.g., no 'g' prefix).  */
43 #define PROGRAM_NAME "coreutils"
44 
45 #define AUTHORS \
46   proper_name ("Alex Deymo")
47 
48 static struct option const long_options[] =
49 {
50   {GETOPT_HELP_OPTION_DECL},
51   {GETOPT_VERSION_OPTION_DECL},
52   {nullptr, 0, nullptr, 0}
53 };
54 
55 
56 void
usage(int status)57 usage (int status)
58 {
59   if (status != EXIT_SUCCESS)
60     emit_try_help ();
61   else
62     {
63       printf (_("\
64 Usage: %s --coreutils-prog=PROGRAM_NAME [PARAMETERS]... \n"),
65               program_name);
66       fputs (_("\
67 Execute the PROGRAM_NAME built-in program with the given PARAMETERS.\n\
68 \n"), stdout);
69       fputs (HELP_OPTION_DESCRIPTION, stdout);
70       fputs (VERSION_OPTION_DESCRIPTION, stdout);
71 
72 #ifdef SINGLE_BINARY
73 /* XXX: Ideally we'd like to present "install" here, not "ginstall".  */
74       char const *prog_name_list =
75 # define SINGLE_BINARY_PROGRAM(prog_name_str, main_name) " " prog_name_str
76 # include "coreutils.h"
77 # undef SINGLE_BINARY_PROGRAM
78       ;
79       printf ("\n\
80 Built-in programs:\n\
81 %s\n", prog_name_list);
82 #endif
83 
84       printf (_("\
85 \n\
86 Use: '%s --coreutils-prog=PROGRAM_NAME --help' for individual program help.\n"),
87               program_name);
88       emit_ancillary_info (PROGRAM_NAME);
89     }
90   exit (status);
91 }
92 
93 static void
launch_program(char const * prog_name,int prog_argc,char ** prog_argv)94 launch_program (char const *prog_name, int prog_argc, char **prog_argv)
95 {
96   int (*prog_main) (int, char **) = nullptr;
97 
98   /* Ensure that at least one parameter was passed.  */
99   if (!prog_argc || !prog_argv || !prog_argv[0] || !prog_name)
100     return;
101 
102 #ifdef SINGLE_BINARY
103   if (false);
104   /* Look up the right main program.  */
105 # define SINGLE_BINARY_PROGRAM(prog_name_str, main_name) \
106   else if (STREQ (prog_name_str, prog_name)) \
107     prog_main = single_binary_main_##main_name;
108 # include "coreutils.h"
109 # undef SINGLE_BINARY_PROGRAM
110 #endif
111 
112   if (! prog_main)
113     return;
114 
115 #if HAVE_PRCTL && defined PR_SET_NAME
116   /* Not being able to set the program name is not a fatal error.  */
117   prctl (PR_SET_NAME, prog_argv[0]);
118 #endif
119 #if HAVE_PRCTL && defined PR_SET_MM_ARG_START
120   /* Shift the beginning of the command line to prog_argv[0] (if set) so
121      /proc/$pid/cmdline reflects a more specific value.  Note one needs
122      CAP_SYS_RESOURCE or root privileges for this to succeed.  */
123   prctl (PR_SET_MM, PR_SET_MM_ARG_START, prog_argv[0], 0, 0);
124 #endif
125 
126   exit (prog_main (prog_argc, prog_argv));
127 }
128 
129 int
main(int argc,char ** argv)130 main (int argc, char **argv)
131 {
132   char *prog_name = last_component (argv[0]);
133   int optc;
134 
135   /* Map external name to internal name.  */
136   char ginstall[] = "ginstall";
137   if (STREQ (prog_name, "install"))
138     prog_name = ginstall;
139 
140   /* If this program is called directly as "coreutils" or if the value of
141      argv[0] is an unknown tool (which "coreutils" is), we proceed and parse
142      the options.  */
143   launch_program (prog_name, argc, argv);
144 
145   /* No known program was selected via argv[0].  Try parsing the first
146      argument as --coreutils-prog=PROGRAM to determine the program.  The
147      invocation for this case should be:
148        path/to/coreutils --coreutils-prog=someprog someprog ...
149      The third argument is what the program will see as argv[0].  */
150 
151   if (argc >= 2)
152     {
153       size_t nskip = 0;
154       char *arg_name = nullptr;
155 
156       /* If calling coreutils directly, the "script" name isn't passed.
157          Distinguish the two cases with a -shebang suffix.  */
158       if (STRPREFIX (argv[1], "--coreutils-prog="))
159         {
160           nskip = 1;
161           arg_name = prog_name = argv[1] + strlen ("--coreutils-prog=");
162         }
163       else if (STRPREFIX (argv[1], "--coreutils-prog-shebang="))
164         {
165           nskip = 2;
166           prog_name = argv[1] + strlen ("--coreutils-prog-shebang=");
167           if (argc >= 3)
168             arg_name = last_component (argv[2]);
169           else
170             arg_name = prog_name;
171         }
172 
173       if (nskip)
174         {
175           argv[nskip] = arg_name; /* XXX: Discards any specified path.  */
176           launch_program (prog_name, argc - nskip, argv + nskip);
177           error (EXIT_FAILURE, 0, _("unknown program %s"),
178                  quote (prog_name));
179         }
180     }
181 
182   /* No known program was selected.  From here on, we behave like any other
183      coreutils program.  */
184   initialize_main (&argc, &argv);
185   set_program_name (argv[0]);
186   setlocale (LC_ALL, "");
187   bindtextdomain (PACKAGE, LOCALEDIR);
188   textdomain (PACKAGE);
189   atexit (close_stdout);
190 
191   if ((optc = getopt_long (argc, argv, "", long_options, nullptr)) != -1)
192     switch (optc)
193       {
194       case_GETOPT_HELP_CHAR;
195 
196       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
197       }
198 
199   /* Only print the error message when no options have been passed
200      to coreutils.  */
201   if (optind == 1 && prog_name && !STREQ (prog_name, "coreutils"))
202     error (0, 0, _("unknown program %s"),
203            quote (prog_name));
204 
205   usage (EXIT_FAILURE);
206 }
207