1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "jsvc.h"
18 #include <limits.h>
19 #include <glob.h>
20 
21 /* Return the argument of a command line option */
optional(int argc,char * argv[],int argi)22 static char *optional(int argc, char *argv[], int argi)
23 {
24 
25     argi++;
26     if (argi >= argc)
27         return NULL;
28     if (argv[argi] == NULL)
29         return NULL;
30     if (argv[argi][0] == '-')
31         return NULL;
32     return strdup(argv[argi]);
33 }
34 
memstrcat(char * ptr,const char * str,const char * add)35 static char *memstrcat(char *ptr, const char *str, const char *add)
36 {
37     size_t nl = 1;
38     int nas = ptr == NULL;
39     if (ptr)
40         nl += strlen(ptr);
41     if (str)
42         nl += strlen(str);
43     if (add)
44         nl += strlen(add);
45     ptr = (char *)realloc(ptr, nl);
46     if (ptr) {
47         if (nas)
48             *ptr = '\0';
49         if (str)
50             strcat(ptr, str);
51         if (add)
52             strcat(ptr, add);
53     }
54     return ptr;
55 }
56 
eval_ppath(char * strcp,const char * pattern)57 static char *eval_ppath(char *strcp, const char *pattern)
58 {
59     glob_t globbuf;
60     char jars[PATH_MAX + 1];
61 
62     if (strlen(pattern) > (sizeof(jars) - 5)) {
63         return memstrcat(strcp, pattern, NULL);
64     }
65     strcpy(jars, pattern);
66     strcat(jars, ".jar");
67     memset(&globbuf, 0, sizeof(glob_t));
68     if (glob(jars, GLOB_ERR, NULL, &globbuf) == 0) {
69         size_t n;
70         for (n = 0; n < globbuf.gl_pathc - 1; n++) {
71             strcp = memstrcat(strcp, globbuf.gl_pathv[n], ":");
72             if (strcp == NULL) {
73                 globfree(&globbuf);
74                 return NULL;
75             }
76         }
77         strcp = memstrcat(strcp, globbuf.gl_pathv[n], NULL);
78         globfree(&globbuf);
79     }
80     return strcp;
81 }
82 
83 #define JAVA_CLASSPATH      "-Djava.class.path="
84 /**
85  * Call glob on each PATH like string path.
86  * Glob is called only if the part ends with asterisk in which
87  * case asterisk is replaced by *.jar when searching
88  */
eval_cpath(const char * cp)89 static char *eval_cpath(const char *cp)
90 {
91     char *cpy = memstrcat(NULL, JAVA_CLASSPATH, cp);
92     char *gcp = NULL;
93     char *pos;
94     char *ptr;
95 
96     if (!cpy)
97         return NULL;
98     ptr = cpy + sizeof(JAVA_CLASSPATH) - 1;;
99     while ((pos = strchr(ptr, ':'))) {
100         *pos = '\0';
101         if (gcp)
102             gcp = memstrcat(gcp, ":", NULL);
103         else
104             gcp = memstrcat(NULL, JAVA_CLASSPATH, NULL);
105         if ((pos > ptr) && (*(pos - 1) == '*')) {
106             if (!(gcp = eval_ppath(gcp, ptr))) {
107                 /* Error.
108                  * Return the original string processed so far.
109                  */
110                 return cpy;
111             }
112         }
113         else
114             gcp = memstrcat(gcp, ptr, NULL);
115         ptr = pos + 1;
116     }
117     if (*ptr) {
118         size_t end = strlen(ptr);
119         if (gcp)
120             gcp = memstrcat(gcp, ":", NULL);
121         else
122             gcp = memstrcat(NULL, JAVA_CLASSPATH, NULL);
123         if (end > 0 && ptr[end - 1] == '*') {
124             /* Last path elemet ends with star
125              * Do a globbing.
126              */
127             gcp = eval_ppath(gcp, ptr);
128         }
129         else {
130             /* Just add the part */
131             gcp = memstrcat(gcp, ptr, NULL);
132         }
133     }
134     /* Free the allocated copy */
135     if (gcp) {
136         free(cpy);
137         return gcp;
138     }
139     else
140         return cpy;
141 }
142 
143 /* Parse command line arguments */
parse(int argc,char * argv[])144 static arg_data *parse(int argc, char *argv[])
145 {
146     arg_data *args = NULL;
147     char *temp = NULL;
148     char *cmnd = NULL;
149     int x = 0;
150 
151     /* Create the default command line arguments */
152     args = (arg_data *)malloc(sizeof(arg_data));
153     args->pidf = "/var/run/jsvc.pid";  /* The default PID file */
154     args->user = NULL;                 /* No user switching by default */
155     args->dtch = true;                 /* Do detach from parent */
156     args->vers = false;                /* Don't display version */
157     args->help = false;                /* Don't display help */
158     args->chck = false;                /* Don't do a check-only startup */
159     args->stop = false;                /* Stop a running jsvc */
160     args->wait = 10;                   /* Wait until jsvc has started the JVM */
161     args->restarts = -1;               /* Infinite restarts by default */
162     args->install = false;             /* Don't install as a service */
163     args->remove = false;              /* Don't remove the installed service */
164     args->service = false;             /* Don't run as a service */
165     args->name = NULL;                 /* No VM version name */
166     args->home = NULL;                 /* No default JAVA_HOME */
167     args->onum = 0;                    /* Zero arguments, but let's have some room */
168     args->clas = NULL;                 /* No class predefined */
169     args->anum = 0;                    /* Zero class specific arguments but make room */
170     args->cwd = "/";                   /* Use root as default */
171     args->outfile = "/dev/null";       /* Swallow by default */
172     args->errfile = "/dev/null";       /* Swallow by default */
173     args->redirectstdin = true;        /* Redirect stdin to /dev/null by default */
174     args->procname = "jsvc.exec";
175 #ifndef JSVC_UMASK
176     args->umask = 0077;
177 #else
178     args->umask = JSVC_UMASK;
179 #endif
180 
181     if (!(args->args = (char **)malloc(argc * sizeof(char *))))
182         return NULL;
183     if (!(args->opts = (char **)malloc(argc * sizeof(char *))))
184         return NULL;
185 
186     /* Set up the command name */
187     cmnd = strrchr(argv[0], '/');
188     if (cmnd == NULL)
189         cmnd = argv[0];
190     else
191         cmnd++;
192     log_prog = strdup(cmnd);
193 
194     /* Iterate thru command line arguments */
195     for (x = 1; x < argc; x++) {
196 
197         if (!strcmp(argv[x], "-cp") || !strcmp(argv[x], "-classpath")) {
198             temp = optional(argc, argv, x++);
199             if (temp == NULL) {
200                 log_error("Invalid classpath specified");
201                 return NULL;
202             }
203             args->opts[args->onum] = eval_cpath(temp);
204             if (args->opts[args->onum] == NULL) {
205                 log_error("Invalid classpath specified");
206                 return NULL;
207             }
208             free(temp);
209             args->onum++;
210 
211         }
212         else if (!strcmp(argv[x], "-jvm")) {
213             args->name = optional(argc, argv, x++);
214             if (args->name == NULL) {
215                 log_error("Invalid Java VM name specified");
216                 return NULL;
217             }
218         }
219         else if (!strcmp(argv[x], "-client")) {
220             args->name = strdup("client");
221         }
222         else if (!strcmp(argv[x], "-server")) {
223             args->name = strdup("server");
224         }
225         else if (!strcmp(argv[x], "-home") || !strcmp(argv[x], "-java-home")) {
226             args->home = optional(argc, argv, x++);
227             if (args->home == NULL) {
228                 log_error("Invalid Java Home specified");
229                 return NULL;
230             }
231         }
232         else if (!strcmp(argv[x], "-user")) {
233             args->user = optional(argc, argv, x++);
234             if (args->user == NULL) {
235                 log_error("Invalid user name specified");
236                 return NULL;
237             }
238         }
239         else if (!strcmp(argv[x], "-cwd")) {
240             args->cwd = optional(argc, argv, x++);
241             if (args->cwd == NULL) {
242                 log_error("Invalid working directory specified");
243                 return NULL;
244             }
245         }
246         else if (!strcmp(argv[x], "-version")) {
247             args->vers = true;
248             args->dtch = false;
249         }
250         else if (!strcmp(argv[x], "-showversion")) {
251             args->vershow = true;
252         }
253         else if (!strcmp(argv[x], "-?") || !strcmp(argv[x], "-help") || !strcmp(argv[x], "--help")) {
254             args->help = true;
255             args->dtch = false;
256             return args;
257         }
258         else if (!strcmp(argv[x], "-X")) {
259             log_error("Option -X currently unsupported");
260             log_error("Please use \"java -X\" to see your extra VM options");
261         }
262         else if (!strcmp(argv[x], "-debug")) {
263             log_debug_flag = true;
264         }
265         else if (!strcmp(argv[x], "-wait")) {
266             temp = optional(argc, argv, x++);
267             if (temp)
268                 args->wait = atoi(temp);
269         }
270         else if (!strcmp(argv[x], "-restarts")) {
271             temp = optional(argc, argv, x++);
272             if (temp)
273                 args->restarts = atoi(temp);
274             if (args->restarts < -1) {
275                 log_error("Invalid max restarts [-1,0,...)");
276                 return NULL;
277             }
278         }
279         else if (!strcmp(argv[x], "-umask")) {
280             temp = optional(argc, argv, x++);
281             if (temp == NULL) {
282                 log_error("Invalid umask specified");
283                 return NULL;
284             }
285             /* Parameter must be in octal */
286             args->umask = (int)strtol(temp, NULL, 8);
287             if (args->umask < 02) {
288                 log_error("Invalid umask specified (min=02)");
289                 return NULL;
290             }
291         }
292         else if (!strcmp(argv[x], "-stop")) {
293             args->stop = true;
294         }
295         else if (!strcmp(argv[x], "-check")) {
296             args->chck = true;
297             args->dtch = false;
298         }
299         else if (!strcmp(argv[x], "-nodetach")) {
300             args->dtch = false;
301         }
302         else if (!strcmp(argv[x], "-keepstdin")) {
303             args->redirectstdin = false;
304         }
305         else if (!strcmp(argv[x], "-service")) {
306             args->service = true;
307         }
308         else if (!strcmp(argv[x], "-install")) {
309             args->install = true;
310         }
311         else if (!strcmp(argv[x], "-remove")) {
312             args->remove = true;
313         }
314         else if (!strcmp(argv[x], "-pidfile")) {
315             args->pidf = optional(argc, argv, x++);
316             if (args->pidf == NULL) {
317                 log_error("Invalid PID file specified");
318                 return NULL;
319             }
320         }
321         else if (!strcmp(argv[x], "-outfile")) {
322             args->outfile = optional(argc, argv, x++);
323             if (args->outfile == NULL) {
324                 log_error("Invalid Output File specified");
325                 return NULL;
326             }
327         }
328         else if (!strcmp(argv[x], "-errfile")) {
329             args->errfile = optional(argc, argv, x++);
330             if (args->errfile == NULL) {
331                 log_error("Invalid Error File specified");
332                 return NULL;
333             }
334         }
335         else if (!strncmp(argv[x], "-verbose", 8)) {
336             args->opts[args->onum++] = strdup(argv[x]);
337         }
338         else if (!strcmp(argv[x], "-D")) {
339             log_error("Parameter -D must be followed by <name>=<value>");
340             return NULL;
341         }
342         else if (!strncmp(argv[x], "-D", 2)) {
343             temp = strchr(argv[x], '=');
344             if (temp == argv[x] + 2) {
345                 log_error("A property name must be specified before '='");
346                 return NULL;
347             }
348             args->opts[args->onum++] = strdup(argv[x]);
349         }
350         else if (!strncmp(argv[x], "-X", 2)) {
351             args->opts[args->onum++] = strdup(argv[x]);
352         }
353         else if (!strncmp(argv[x], "-ea", 3)) {
354             args->opts[args->onum++] = strdup(argv[x]);
355         }
356         else if (!strncmp(argv[x], "-enableassertions", 17)) {
357             args->opts[args->onum++] = strdup(argv[x]);
358         }
359         else if (!strncmp(argv[x], "-da", 3)) {
360             args->opts[args->onum++] = strdup(argv[x]);
361         }
362         else if (!strncmp(argv[x], "-disableassertions", 18)) {
363             args->opts[args->onum++] = strdup(argv[x]);
364         }
365         else if (!strcmp(argv[x], "-esa")) {
366             args->opts[args->onum++] = strdup(argv[x]);
367         }
368         else if (!strcmp(argv[x], "-enablesystemassertions")) {
369             args->opts[args->onum++] = strdup(argv[x]);
370         }
371         else if (!strcmp(argv[x], "-dsa")) {
372             args->opts[args->onum++] = strdup(argv[x]);
373         }
374         else if (!strcmp(argv[x], "-disablesystemassertions")) {
375             args->opts[args->onum++] = strdup(argv[x]);
376         }
377         else if (!strcmp(argv[x], "-procname")) {
378             args->procname = optional(argc, argv, x++);
379             if (args->procname == NULL) {
380                 log_error("Invalid process name specified");
381                 return NULL;
382             }
383         }
384         else if (!strncmp(argv[x], "-agentlib:", 10)) {
385             args->opts[args->onum++] = strdup(argv[x]);
386         }
387         else if (!strncmp(argv[x], "-agentpath:", 11)) {
388             args->opts[args->onum++] = strdup(argv[x]);
389         }
390         else if (!strncmp(argv[x], "-javaagent:", 11)) {
391             args->opts[args->onum++] = strdup(argv[x]);
392         }
393         /* Java 9 specific options */
394         else if (!strncmp(argv[x], "--add-modules=", 14)) {
395             args->opts[args->onum++] = strdup(argv[x]);
396         }
397         else if (!strncmp(argv[x], "--module-path=", 14)) {
398             args->opts[args->onum++] = strdup(argv[x]);
399         }
400         else if (!strncmp(argv[x], "--upgrade-module-path=", 22)) {
401             args->opts[args->onum++] = strdup(argv[x]);
402         }
403         else if (!strncmp(argv[x], "--add-reads=", 12)) {
404             args->opts[args->onum++] = strdup(argv[x]);
405         }
406         else if (!strncmp(argv[x], "--add-exports=", 14)) {
407             args->opts[args->onum++] = strdup(argv[x]);
408         }
409         else if (!strncmp(argv[x], "--add-opens=", 12)) {
410             args->opts[args->onum++] = strdup(argv[x]);
411         }
412         else if (!strncmp(argv[x], "--limit-modules=", 16)) {
413             args->opts[args->onum++] = strdup(argv[x]);
414         }
415         else if (!strncmp(argv[x], "--patch-module=", 15)) {
416             args->opts[args->onum++] = strdup(argv[x]);
417         }
418         else if (!strncmp(argv[x], "--illegal-access=", 17)) {
419             args->opts[args->onum++] = strdup(argv[x]);
420         }
421         /* Java 11 specific options */
422         else if (!strncmp(argv[x], "--enable-preview", 16)) {
423             args->opts[args->onum++] = strdup(argv[x]);
424         }
425         else if (*argv[x] == '-') {
426             log_error("Invalid option %s", argv[x]);
427             return NULL;
428         }
429         else {
430             args->clas = strdup(argv[x]);
431             break;
432         }
433     }
434 
435     if (args->clas == NULL && args->remove == false) {
436         log_error("No class specified");
437         return NULL;
438     }
439 
440     x++;
441     while (x < argc) {
442         args->args[args->anum++] = strdup(argv[x++]);
443     }
444     return args;
445 }
446 
IsYesNo(bool par)447 static const char *IsYesNo(bool par)
448 {
449     switch (par) {
450         case false:
451             return "No";
452         case true:
453             return "Yes";
454     }
455     return "[Error]";
456 }
457 
IsTrueFalse(bool par)458 static const char *IsTrueFalse(bool par)
459 {
460     switch (par) {
461         case false:
462             return "False";
463         case true:
464             return "True";
465     }
466     return "[Error]";
467 }
468 
IsEnabledDisabled(bool par)469 static const char *IsEnabledDisabled(bool par)
470 {
471     switch (par) {
472         case true:
473             return "Enabled";
474         case false:
475             return "Disabled";
476     }
477     return "[Error]";
478 }
479 
480 /* Main entry point: parse command line arguments and dump them */
arguments(int argc,char * argv[])481 arg_data *arguments(int argc, char *argv[])
482 {
483     arg_data *args = parse(argc, argv);
484     int x = 0;
485 
486     if (args == NULL) {
487         log_error("Cannot parse command line arguments");
488         return NULL;
489     }
490 
491     if (log_debug_flag == true) {
492         log_debug("+-- DUMPING PARSED COMMAND AND ARGUMENTS ---------------");
493         log_debug("| Executable:      %s", PRINT_NULL(argv[0]));
494         log_debug("| Detach:          %s", IsTrueFalse(args->dtch));
495         log_debug("| Show Version:    %s", IsYesNo(args->vers));
496         log_debug("| Show Help:       %s", IsYesNo(args->help));
497         log_debug("| Check Only:      %s", IsEnabledDisabled(args->chck));
498         log_debug("| Stop:            %s", IsTrueFalse(args->stop));
499         log_debug("| Wait:            %d", args->wait);
500         log_debug("| Restarts:        %d", args->restarts);
501         log_debug("| Run as service:  %s", IsYesNo(args->service));
502         log_debug("| Install service: %s", IsYesNo(args->install));
503         log_debug("| Remove service:  %s", IsYesNo(args->remove));
504         log_debug("| JVM Name:        \"%s\"", PRINT_NULL(args->name));
505         log_debug("| Java Home:       \"%s\"", PRINT_NULL(args->home));
506         log_debug("| PID File:        \"%s\"", PRINT_NULL(args->pidf));
507         log_debug("| User Name:       \"%s\"", PRINT_NULL(args->user));
508         log_debug("| Extra Options:   %d", args->onum);
509         for (x = 0; x < args->onum; x++) {
510             log_debug("|   \"%s\"", args->opts[x]);
511         }
512 
513         log_debug("| Class Invoked:   \"%s\"", PRINT_NULL(args->clas));
514         log_debug("| Class Arguments: %d", args->anum);
515         for (x = 0; x < args->anum; x++) {
516             log_debug("|   \"%s\"", args->args[x]);
517         }
518         log_debug("+-------------------------------------------------------");
519     }
520     return args;
521 }
522