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