55
55
56
56
#ident "$Id$"
57
57
58
- #include <getopt.h>
59
58
#include <grp.h>
60
59
#include <pwd.h>
61
60
#include <signal.h>
@@ -91,10 +90,12 @@ static bool caller_on_console = false;
91
90
static /*@only@*/ char * caller_pass ;
92
91
#endif
93
92
#endif /* !USE_PAM */
94
- static bool doshell = false;
93
+ static bool do_interactive_shell = false;
95
94
static bool fakelogin = false;
96
95
static /*@observer@*/ const char * shellstr ;
97
96
static /*@null@*/ char * command = NULL ;
97
+ static /*@null@*/ char * exec_command = NULL ;
98
+ static int optidx ;
98
99
99
100
100
101
/* not needed by sulog.c anymore */
@@ -327,11 +328,11 @@ static void prepare_pam_close_session (void)
327
328
if ( (sigaddset (& ourset , SIGTERM ) != 0 )
328
329
|| (sigaddset (& ourset , SIGALRM ) != 0 )
329
330
|| (sigaction (SIGTERM , & action , NULL ) != 0 )
330
- || ( ! doshell /* handle SIGINT (Ctrl-C), SIGQUIT
331
- * (Ctrl-\), and SIGTSTP (Ctrl-Z)
332
- * since the child will not control
333
- * the tty.
334
- */
331
+ || (! do_interactive_shell /* handle SIGINT (Ctrl-C), SIGQUIT
332
+ * (Ctrl-\), and SIGTSTP (Ctrl-Z)
333
+ * since the child will not control
334
+ * the tty.
335
+ */
335
336
&& ( (sigaddset (& ourset , SIGINT ) != 0 )
336
337
|| (sigaddset (& ourset , SIGQUIT ) != 0 )
337
338
|| (sigaddset (& ourset , SIGTSTP ) != 0 )
@@ -440,12 +441,14 @@ static void usage (int status)
440
441
"\n"
441
442
"Options:\n"
442
443
" -c, --command COMMAND pass COMMAND to the invoked shell\n"
444
+ " -e, --exec PATH run PATH without shell, follow -- with args\n"
443
445
" -h, --help display this help message and exit\n"
444
446
" -, -l, --login make the shell a login shell\n"
445
447
" -m, -p,\n"
446
448
" --preserve-environment do not reset environment variables, and\n"
447
449
" keep the same shell\n"
448
450
" -s, --shell SHELL use SHELL instead of the default in passwd\n"
451
+ " -- pass all subsequent arguments on as-is\n"
449
452
"\n"
450
453
"If no username is given, assume root.\n" ), (E_SUCCESS != status ) ? stderr : stdout );
451
454
exit (status );
@@ -760,6 +763,48 @@ static void save_caller_context (char **argv)
760
763
pw_free (pw );
761
764
}
762
765
766
+ /*
767
+ * flags_match - test arg against flag candidates
768
+ */
769
+ static bool flags_match (const char * arg , const char * a , const char * b , const char * c )
770
+ {
771
+ return (a != NULL && strcmp (arg , a ) == 0 ) ||
772
+ (b != NULL && strcmp (arg , b ) == 0 ) ||
773
+ (c != NULL && strcmp (arg , c ) == 0 );
774
+ }
775
+
776
+ /* is_flag_like - test if arg resembles a flag
777
+ *
778
+ * lone "--" and bare leading-hyphen-less words are not flag-like,
779
+ * everything else is considered a probable flag.
780
+ */
781
+ static bool is_flag_like (const char * arg )
782
+ {
783
+ if (arg [0 ] != '-' )
784
+ return false;
785
+
786
+ if (strcmp (arg , "--" ) == 0 )
787
+ return false;
788
+
789
+ return true;
790
+ }
791
+
792
+ static void flag_arg_required (const char * arg )
793
+ {
794
+ fprintf (stderr ,
795
+ _ ("%s: option \'%s\' requires an argument\n" ),
796
+ Prog , arg );
797
+ usage (E_USAGE );
798
+ }
799
+
800
+ static void flag_unknown (const char * arg )
801
+ {
802
+ fprintf (stderr ,
803
+ _ ("%s: unrecognized option \'%s\'\n" ),
804
+ Prog , arg );
805
+ usage (E_BAD_ARG );
806
+ }
807
+
763
808
/*
764
809
* process_flags - Process the command line arguments
765
810
*
@@ -769,51 +814,58 @@ static void save_caller_context (char **argv)
769
814
*/
770
815
static void process_flags (int argc , char * * argv )
771
816
{
772
- int c ;
773
- static struct option long_options [] = {
774
- {"command" , required_argument , NULL , 'c' },
775
- {"help" , no_argument , NULL , 'h' },
776
- {"login" , no_argument , NULL , 'l' },
777
- {"preserve-environment" , no_argument , NULL , 'p' },
778
- {"shell" , required_argument , NULL , 's' },
779
- {NULL , 0 , NULL , '\0' }
780
- };
781
-
782
- while ((c = getopt_long (argc , argv , "c:hlmps:" ,
783
- long_options , NULL )) != -1 ) {
784
- switch (c ) {
785
- case 'c' :
786
- command = optarg ;
787
- break ;
788
- case 'h' :
817
+ for (optidx = 1 ; optidx < argc ; optidx ++ ) {
818
+ const char * arg = argv [optidx ];
819
+
820
+ if (flags_match (arg , "--command" , "-c" , NULL )) {
821
+ if (optidx == argc - 1 ) {
822
+ flag_arg_required (arg );
823
+ }
824
+
825
+ command = argv [++ optidx ];
826
+ } else if (flags_match (arg , "--exec" , "-e" , NULL )) {
827
+ if (optidx == argc - 1 ) {
828
+ flag_arg_required (arg );
829
+ }
830
+
831
+ exec_command = argv [++ optidx ];
832
+ } else if (flags_match (arg , "--help" , "-h" , NULL )) {
789
833
usage (E_SUCCESS );
790
- break ;
791
- case 'l' :
834
+ } else if (flags_match (arg , "--login" , "-l" , "-" )) {
792
835
fakelogin = true;
793
- break ;
794
- case 'm' :
795
- case 'p' :
836
+ } else if (flags_match (arg , "--preserve-environment" , "-p" , "-m" )) {
796
837
/* This will only have an effect if the target
797
838
* user do not have a restricted shell, or if
798
839
* su is called by root.
799
840
*/
800
841
change_environment = false;
842
+ } else if (flags_match (arg , "--shell" , "-s" , NULL )) {
843
+ if (optidx == argc - 1 ) {
844
+ flag_arg_required (arg );
845
+ }
846
+
847
+ shellstr = argv [++ optidx ];
848
+ } else if (is_flag_like (arg )) {
849
+ flag_unknown (arg );
850
+ } else {
801
851
break ;
802
- case 's' :
803
- shellstr = optarg ;
804
- break ;
805
- default :
806
- usage (E_USAGE ); /* NOT REACHED */
807
852
}
808
853
}
809
854
810
- if ((optind < argc ) && (strcmp (argv [optind ], "-" ) == 0 )) {
811
- fakelogin = true;
812
- optind ++ ;
855
+ if (NULL != exec_command && NULL != command ) {
856
+ fprintf (stderr ,
857
+ _ ("%s: COMMAND and PATH are mutually exclusive\n" ),
858
+ argv [0 ]);
859
+ usage (E_USAGE );
860
+ }
861
+
862
+ if (NULL != exec_command ) {
863
+ command = exec_command ;
813
864
}
814
865
815
- if (optind < argc ) {
816
- STRFCPY (name , argv [optind ++ ]); /* use this login id */
866
+ /* if next arg is not "--", treat as USER */
867
+ if (optidx < argc && strcmp (argv [optidx ], "--" )) {
868
+ STRFCPY (name , argv [optidx ++ ]); /* use this login id */
817
869
}
818
870
if ('\0' == name [0 ]) { /* use default user */
819
871
struct passwd * root_pw = getpwnam ("root" );
@@ -829,9 +881,14 @@ static void process_flags (int argc, char **argv)
829
881
}
830
882
}
831
883
832
- doshell = (argc == optind ); /* any arguments remaining? */
884
+ /* if more and next arg is "--", skip it and leave rest as-is */
885
+ if (optidx < argc && strcmp (argv [optidx ], "--" ) == 0 ) {
886
+ optidx ++ ;
887
+ }
888
+
889
+ do_interactive_shell = (argc == optidx ); /* any arguments remaining? */
833
890
if (NULL != command ) {
834
- doshell = false;
891
+ do_interactive_shell = false;
835
892
}
836
893
}
837
894
@@ -1107,7 +1164,7 @@ int main (int argc, char **argv)
1107
1164
1108
1165
set_environment (pw );
1109
1166
1110
- if (!doshell ) {
1167
+ if (!do_interactive_shell ) {
1111
1168
/* There is no need for a controlling terminal.
1112
1169
* This avoids the callee to inject commands on
1113
1170
* the caller's tty. */
@@ -1175,10 +1232,10 @@ int main (int argc, char **argv)
1175
1232
cp = Basename (shellstr );
1176
1233
}
1177
1234
1178
- if (!doshell ) {
1235
+ if (!do_interactive_shell ) {
1179
1236
int err ;
1180
1237
/* Position argv to the remaining arguments */
1181
- argv += optind ;
1238
+ argv += optidx ;
1182
1239
if (NULL != command ) {
1183
1240
argv -= 2 ;
1184
1241
argv [0 ] = "-c" ;
@@ -1189,10 +1246,18 @@ int main (int argc, char **argv)
1189
1246
* with the rest of the command line included.
1190
1247
*/
1191
1248
argv [-1 ] = cp ;
1192
- execve_shell (shellstr , & argv [-1 ], environ );
1193
- err = errno ;
1194
- (void ) fprintf (stderr ,
1195
- _ ("Cannot execute %s\n" ), shellstr );
1249
+
1250
+ if (NULL != exec_command ) {
1251
+ (void ) execve (command , & argv [1 ], environ );
1252
+ err = errno ;
1253
+ (void ) fprintf (stderr ,
1254
+ _ ("Cannot execute \'%s\'\n" ), command );
1255
+ } else {
1256
+ execve_shell (shellstr , & argv [-1 ], environ );
1257
+ err = errno ;
1258
+ (void ) fprintf (stderr ,
1259
+ _ ("Cannot execute \'%s\'\n" ), shellstr );
1260
+ }
1196
1261
errno = err ;
1197
1262
} else {
1198
1263
(void ) shell (shellstr , cp , environ );
0 commit comments