Print this page
make: use the more modern wchar routines, not widec.h
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/make/lib/mksh/dosys.cc
+++ new/usr/src/cmd/make/lib/mksh/dosys.cc
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 25
26 26
27 27 /*
28 28 * dosys.cc
29 29 *
30 30 * Execute one commandline
31 31 */
32 32
33 33 /*
34 34 * Included files
35 35 */
36 36 #include <sys/wait.h> /* WIFEXITED(status) */
37 37 #include <alloca.h> /* alloca() */
38 38
39 39 #include <stdio.h> /* errno */
40 40 #include <errno.h> /* errno */
41 41 #include <fcntl.h> /* open() */
42 42 #include <mksh/dosys.h>
43 43 #include <mksh/macro.h> /* getvar() */
44 44 #include <mksh/misc.h> /* getmem(), fatal_mksh(), errmsg() */
45 45 #include <sys/signal.h> /* SIG_DFL */
46 46 #include <sys/stat.h> /* open() */
47 47 #include <sys/wait.h> /* wait() */
48 48 #include <ulimit.h> /* ulimit() */
49 49 #include <unistd.h> /* close(), dup2() */
50 50 #include <libintl.h>
51 51
52 52 /*
53 53 * typedefs & structs
54 54 */
55 55
56 56 /*
57 57 * Static variables
58 58 */
59 59
60 60 /*
61 61 * File table of contents
62 62 */
63 63 static Boolean exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error, pathpt vroot_path);
64 64
65 65 /*
66 66 * Workaround for NFS bug. Sometimes, when running 'open' on a remote
67 67 * dmake server, it fails with "Stale NFS file handle" error.
68 68 * The second attempt seems to work.
69 69 */
70 70 int
71 71 my_open(const char *path, int oflag, mode_t mode) {
72 72 int res = open(path, oflag, mode);
73 73 if (res < 0 && (errno == ESTALE || errno == EAGAIN)) {
74 74 /* Stale NFS file handle. Try again */
75 75 res = open(path, oflag, mode);
76 76 }
77 77 return res;
78 78 }
79 79
80 80 /*
81 81 * void
82 82 * redirect_io(char *stdout_file, char *stderr_file)
83 83 *
84 84 * Redirects stdout and stderr for a child mksh process.
85 85 */
86 86 void
87 87 redirect_io(char *stdout_file, char *stderr_file)
88 88 {
89 89 long descriptor_limit;
90 90 int i;
91 91
92 92 if ((descriptor_limit = ulimit(UL_GDESLIM)) < 0) {
93 93 fatal_mksh(gettext("ulimit() failed: %s"), errmsg(errno));
94 94 }
95 95 for (i = 3; i < descriptor_limit; i++) {
96 96 (void) close(i);
97 97 }
98 98 if ((i = my_open(stdout_file,
99 99 O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC,
100 100 S_IREAD | S_IWRITE)) < 0) {
101 101 fatal_mksh(gettext("Couldn't open standard out temp file `%s': %s"),
102 102 stdout_file,
103 103 errmsg(errno));
104 104 } else {
105 105 if (dup2(i, 1) == -1) {
106 106 fatal_mksh("*** Error: dup2(3, 1) failed: %s",
107 107 errmsg(errno));
108 108 }
109 109 close(i);
110 110 }
111 111 if (stderr_file == NULL) {
112 112 if (dup2(1, 2) == -1) {
113 113 fatal_mksh("*** Error: dup2(1, 2) failed: %s",
114 114 errmsg(errno));
115 115 }
116 116 } else if ((i = my_open(stderr_file,
117 117 O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC,
118 118 S_IREAD | S_IWRITE)) < 0) {
119 119 fatal_mksh(gettext("Couldn't open standard error temp file `%s': %s"),
120 120 stderr_file,
121 121 errmsg(errno));
122 122 } else {
123 123 if (dup2(i, 2) == -1) {
124 124 fatal_mksh("*** Error: dup2(3, 2) failed: %s",
125 125 errmsg(errno));
126 126 }
127 127 close(i);
128 128 }
129 129 }
130 130
131 131 /*
132 132 * doshell(command, ignore_error)
133 133 *
134 134 * Used to run command lines that include shell meta-characters.
135 135 * The make macro SHELL is supposed to contain a path to the shell.
136 136 *
137 137 * Return value:
138 138 * The pid of the process we started
139 139 *
140 140 * Parameters:
141 141 * command The command to run
142 142 * ignore_error Should we abort on error?
143 143 *
144 144 * Global variables used:
145 145 * filter_stderr If -X is on we redirect stderr
146 146 * shell_name The Name "SHELL", used to get the path to shell
147 147 */
148 148 int
149 149 doshell(wchar_t *command, register Boolean ignore_error, char *stdout_file, char *stderr_file, int nice_prio)
150 150 {
151 151 char *argv[6];
152 152 int argv_index = 0;
153 153 int cmd_argv_index;
154 154 int length;
155 155 char nice_prio_buf[MAXPATHLEN];
156 156 register Name shell = getvar(shell_name);
157 157 register char *shellname;
158 158 char *tmp_mbs_buffer;
159 159
160 160
161 161 if (IS_EQUAL(shell->string_mb, "")) {
162 162 shell = shell_name;
163 163 }
164 164 if ((shellname = strrchr(shell->string_mb, (int) slash_char)) == NULL) {
165 165 shellname = shell->string_mb;
166 166 } else {
167 167 shellname++;
168 168 }
169 169
170 170 /*
171 171 * Only prepend the /usr/bin/nice command to the original command
↓ open down ↓ |
171 lines elided |
↑ open up ↑ |
172 172 * if the nice priority, nice_prio, is NOT zero (0).
173 173 * Nice priorities can be a positive or a negative number.
174 174 */
175 175 if (nice_prio != 0) {
176 176 argv[argv_index++] = (char *)"nice";
177 177 (void) sprintf(nice_prio_buf, "-%d", nice_prio);
178 178 argv[argv_index++] = strdup(nice_prio_buf);
179 179 }
180 180 argv[argv_index++] = shellname;
181 181 argv[argv_index++] = (char*)(ignore_error ? "-c" : "-ce");
182 - if ((length = wslen(command)) >= MAXPATHLEN) {
182 + if ((length = wcslen(command)) >= MAXPATHLEN) {
183 183 tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1);
184 184 (void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1);
185 185 cmd_argv_index = argv_index;
186 186 argv[argv_index++] = strdup(tmp_mbs_buffer);
187 187 retmem_mb(tmp_mbs_buffer);
188 188 } else {
189 189 WCSTOMBS(mbs_buffer, command);
190 190 cmd_argv_index = argv_index;
191 191 argv[argv_index++] = strdup(mbs_buffer);
192 192 }
193 193 argv[argv_index] = NULL;
194 194 (void) fflush(stdout);
195 195 if ((childPid = fork()) == 0) {
196 196 enable_interrupt((void (*) (int)) SIG_DFL);
197 197 #if 0
198 198 if (filter_stderr) {
199 199 redirect_stderr();
200 200 }
201 201 #endif
202 202 if (nice_prio != 0) {
203 203 (void) execve("/usr/bin/nice", argv, environ);
204 204 fatal_mksh(gettext("Could not load `/usr/bin/nice': %s"),
205 205 errmsg(errno));
206 206 } else {
207 207 (void) execve(shell->string_mb, argv, environ);
208 208 fatal_mksh(gettext("Could not load Shell from `%s': %s"),
209 209 shell->string_mb,
210 210 errmsg(errno));
211 211 }
212 212 }
213 213 if (childPid == -1) {
214 214 fatal_mksh(gettext("fork failed: %s"),
215 215 errmsg(errno));
216 216 }
217 217 retmem_mb(argv[cmd_argv_index]);
218 218 return childPid;
219 219 }
220 220
221 221 /*
222 222 * exec_vp(name, argv, envp, ignore_error)
223 223 *
224 224 * Like execve, but does path search.
225 225 * This starts command when make invokes it directly (without a shell).
226 226 *
227 227 * Return value:
228 228 * Returns false if the exec failed
229 229 *
230 230 * Parameters:
231 231 * name The name of the command to run
232 232 * argv Arguments for the command
233 233 * envp The environment for it
234 234 * ignore_error Should we abort on error?
235 235 *
236 236 * Global variables used:
237 237 * shell_name The Name "SHELL", used to get the path to shell
238 238 * vroot_path The path used by the vroot package
239 239 */
240 240 static Boolean
241 241 exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error, pathpt vroot_path)
242 242 {
243 243 register Name shell = getvar(shell_name);
244 244 register char *shellname;
245 245 char *shargv[4];
246 246 Name tmp_shell;
247 247
248 248 if (IS_EQUAL(shell->string_mb, "")) {
249 249 shell = shell_name;
250 250 }
251 251
252 252 for (int i = 0; i < 5; i++) {
253 253 (void) execve_vroot(name,
254 254 argv + 1,
255 255 envp,
256 256 vroot_path,
257 257 VROOT_DEFAULT);
258 258 switch (errno) {
259 259 case ENOEXEC:
260 260 case ENOENT:
261 261 /* That failed. Let the shell handle it */
262 262 shellname = strrchr(shell->string_mb, (int) slash_char);
263 263 if (shellname == NULL) {
264 264 shellname = shell->string_mb;
265 265 } else {
266 266 shellname++;
267 267 }
268 268 shargv[0] = shellname;
269 269 shargv[1] = (char*)(ignore_error ? "-c" : "-ce");
270 270 shargv[2] = argv[0];
271 271 shargv[3] = NULL;
272 272 tmp_shell = getvar(shell_name);
273 273 if (IS_EQUAL(tmp_shell->string_mb, "")) {
274 274 tmp_shell = shell_name;
275 275 }
276 276 (void) execve_vroot(tmp_shell->string_mb,
277 277 shargv,
278 278 envp,
279 279 vroot_path,
280 280 VROOT_DEFAULT);
281 281 return failed;
282 282 case ETXTBSY:
283 283 /*
284 284 * The program is busy (debugged?).
285 285 * Wait and then try again.
286 286 */
287 287 (void) sleep((unsigned) i);
288 288 case EAGAIN:
289 289 break;
290 290 default:
291 291 return failed;
292 292 }
293 293 }
294 294 return failed;
295 295 }
296 296
297 297 /*
298 298 * doexec(command, ignore_error)
299 299 *
300 300 * Will scan an argument string and split it into words
301 301 * thus building an argument list that can be passed to exec_ve()
302 302 *
303 303 * Return value:
304 304 * The pid of the process started here
305 305 *
306 306 * Parameters:
307 307 * command The command to run
308 308 * ignore_error Should we abort on error?
309 309 *
310 310 * Global variables used:
311 311 * filter_stderr If -X is on we redirect stderr
312 312 */
313 313 int
314 314 doexec(register wchar_t *command, register Boolean ignore_error, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio)
315 315 {
316 316 int arg_count = 5;
317 317 char **argv;
318 318 int length;
319 319 char nice_prio_buf[MAXPATHLEN];
320 320 register char **p;
321 321 wchar_t *q;
322 322 register wchar_t *t;
323 323 char *tmp_mbs_buffer;
324 324
325 325 /*
326 326 * Only prepend the /usr/bin/nice command to the original command
327 327 * if the nice priority, nice_prio, is NOT zero (0).
328 328 * Nice priorities can be a positive or a negative number.
329 329 */
330 330 if (nice_prio != 0) {
331 331 arg_count += 2;
332 332 }
333 333 for (t = command; *t != (int) nul_char; t++) {
334 334 if (iswspace(*t)) {
↓ open down ↓ |
142 lines elided |
↑ open up ↑ |
335 335 arg_count++;
336 336 }
337 337 }
338 338 argv = (char **)alloca(arg_count * (sizeof(char *)));
339 339 /*
340 340 * Reserve argv[0] for sh in case of exec_vp failure.
341 341 * Don't worry about prepending /usr/bin/nice command to argv[0].
342 342 * In fact, doing it may cause the sh command to fail!
343 343 */
344 344 p = &argv[1];
345 - if ((length = wslen(command)) >= MAXPATHLEN) {
345 + if ((length = wcslen(command)) >= MAXPATHLEN) {
346 346 tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1);
347 347 (void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1);
348 348 argv[0] = strdup(tmp_mbs_buffer);
349 349 retmem_mb(tmp_mbs_buffer);
350 350 } else {
351 351 WCSTOMBS(mbs_buffer, command);
352 352 argv[0] = strdup(mbs_buffer);
353 353 }
354 354
355 355 if (nice_prio != 0) {
356 356 *p++ = strdup("/usr/bin/nice");
357 357 (void) sprintf(nice_prio_buf, "-%d", nice_prio);
358 358 *p++ = strdup(nice_prio_buf);
359 359 }
360 360 /* Build list of argument words. */
361 361 for (t = command; *t;) {
362 362 if (p >= &argv[arg_count]) {
363 363 /* This should never happen, right? */
364 364 WCSTOMBS(mbs_buffer, command);
365 365 fatal_mksh(gettext("Command `%s' has more than %d arguments"),
↓ open down ↓ |
10 lines elided |
↑ open up ↑ |
366 366 mbs_buffer,
367 367 arg_count);
368 368 }
369 369 q = t;
370 370 while (!iswspace(*t) && (*t != (int) nul_char)) {
371 371 t++;
372 372 }
373 373 if (*t) {
374 374 for (*t++ = (int) nul_char; iswspace(*t); t++);
375 375 }
376 - if ((length = wslen(q)) >= MAXPATHLEN) {
376 + if ((length = wcslen(q)) >= MAXPATHLEN) {
377 377 tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1);
378 378 (void) wcstombs(tmp_mbs_buffer, q, (length * MB_LEN_MAX) + 1);
379 379 *p++ = strdup(tmp_mbs_buffer);
380 380 retmem_mb(tmp_mbs_buffer);
381 381 } else {
382 382 WCSTOMBS(mbs_buffer, q);
383 383 *p++ = strdup(mbs_buffer);
384 384 }
385 385 }
386 386 *p = NULL;
387 387
388 388 /* Then exec the command with that argument list. */
389 389 (void) fflush(stdout);
390 390 if ((childPid = fork()) == 0) {
391 391 enable_interrupt((void (*) (int)) SIG_DFL);
392 392 #if 0
393 393 if (filter_stderr) {
394 394 redirect_stderr();
395 395 }
396 396 #endif
397 397 (void) exec_vp(argv[1], argv, environ, ignore_error, vroot_path);
398 398 fatal_mksh(gettext("Cannot load command `%s': %s"), argv[1], errmsg(errno));
399 399 }
400 400 if (childPid == -1) {
401 401 fatal_mksh(gettext("fork failed: %s"),
402 402 errmsg(errno));
403 403 }
404 404 for (int i = 0; argv[i] != NULL; i++) {
405 405 retmem_mb(argv[i]);
406 406 }
407 407 return childPid;
408 408 }
409 409
410 410 /*
411 411 * await(ignore_error, silent_error, target, command, running_pid)
412 412 *
413 413 * Wait for one child process and analyzes
414 414 * the returned status when the child process terminates.
415 415 *
416 416 * Return value:
417 417 * Returns true if commands ran OK
418 418 *
419 419 * Parameters:
420 420 * ignore_error Should we abort on error?
421 421 * silent_error Should error messages be suppressed for dmake?
422 422 * target The target we are building, for error msgs
423 423 * command The command we ran, for error msgs
424 424 * running_pid The pid of the process we are waiting for
425 425 *
426 426 * Static variables used:
427 427 * filter_file The fd for the filter file
428 428 * filter_file_name The name of the filter file
429 429 *
430 430 * Global variables used:
431 431 * filter_stderr Set if -X is on
432 432 */
433 433 Boolean
434 434 await(register Boolean ignore_error, register Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, void *xdrs_p, int job_msg_id)
435 435 {
436 436 int status;
437 437 char *buffer;
438 438 int core_dumped;
439 439 int exit_status;
440 440 FILE *outfp;
441 441 register pid_t pid;
442 442 struct stat stat_buff;
443 443 int termination_signal;
444 444 char tmp_buf[MAXPATHLEN];
445 445
446 446 while ((pid = wait(&status)) != running_pid) {
447 447 if (pid == -1) {
448 448 fatal_mksh(gettext("wait() failed: %s"), errmsg(errno));
449 449 }
450 450 }
451 451 (void) fflush(stdout);
452 452 (void) fflush(stderr);
453 453
454 454 if (status == 0) {
455 455
456 456 #ifdef PRINT_EXIT_STATUS
457 457 warning_mksh("I'm in await(), and status is 0.");
458 458 #endif
459 459
460 460 return succeeded;
461 461 }
462 462
463 463 #ifdef PRINT_EXIT_STATUS
464 464 warning_mksh("I'm in await(), and status is *NOT* 0.");
465 465 #endif
466 466
467 467
468 468 exit_status = WEXITSTATUS(status);
469 469
470 470 #ifdef PRINT_EXIT_STATUS
471 471 warning_mksh("I'm in await(), and exit_status is %d.", exit_status);
472 472 #endif
473 473
474 474 termination_signal = WTERMSIG(status);
475 475 core_dumped = WCOREDUMP(status);
476 476
477 477 /*
478 478 * If the child returned an error, we now try to print a
479 479 * nice message about it.
480 480 */
481 481
482 482 tmp_buf[0] = (int) nul_char;
483 483 if (!silent_error) {
484 484 if (exit_status != 0) {
485 485 (void) fprintf(stdout,
486 486 gettext("*** Error code %d"),
487 487 exit_status);
488 488 } else {
489 489 (void) fprintf(stdout,
490 490 gettext("*** Signal %d"),
491 491 termination_signal);
492 492 if (core_dumped) {
493 493 (void) fprintf(stdout,
494 494 gettext(" - core dumped"));
495 495 }
496 496 }
497 497 if (ignore_error) {
498 498 (void) fprintf(stdout,
499 499 gettext(" (ignored)"));
500 500 }
501 501 (void) fprintf(stdout, "\n");
502 502 (void) fflush(stdout);
503 503 }
504 504
505 505 #ifdef PRINT_EXIT_STATUS
506 506 warning_mksh("I'm in await(), returning failed.");
507 507 #endif
508 508
509 509 return failed;
510 510 }
511 511
512 512 /*
513 513 * sh_command2string(command, destination)
514 514 *
515 515 * Run one sh command and capture the output from it.
516 516 *
517 517 * Return value:
518 518 *
519 519 * Parameters:
520 520 * command The command to run
521 521 * destination Where to deposit the output from the command
522 522 *
523 523 * Static variables used:
524 524 *
525 525 * Global variables used:
526 526 */
527 527 void
528 528 sh_command2string(register String command, register String destination)
529 529 {
530 530 register FILE *fd;
531 531 register int chr;
532 532 int status;
533 533 Boolean command_generated_output = false;
534 534
535 535 command->text.p = (int) nul_char;
536 536 WCSTOMBS(mbs_buffer, command->buffer.start);
537 537 if ((fd = popen(mbs_buffer, "r")) == NULL) {
538 538 WCSTOMBS(mbs_buffer, command->buffer.start);
539 539 fatal_mksh(gettext("Could not run command `%s' for :sh transformation"),
540 540 mbs_buffer);
541 541 }
542 542 while ((chr = getc(fd)) != EOF) {
543 543 if (chr == (int) newline_char) {
544 544 chr = (int) space_char;
545 545 }
546 546 command_generated_output = true;
547 547 append_char(chr, destination);
548 548 }
549 549
550 550 /*
551 551 * We don't want to keep the last LINE_FEED since usually
552 552 * the output of the 'sh:' command is used to evaluate
553 553 * some MACRO. ( /bin/sh and other shell add a line feed
554 554 * to the output so that the prompt appear in the right place.
555 555 * We don't need that
556 556 */
557 557 if (command_generated_output){
558 558 if ( *(destination->text.p-1) == (int) space_char) {
559 559 * (-- destination->text.p) = '\0';
560 560 }
561 561 } else {
562 562 /*
563 563 * If the command didn't generate any output,
564 564 * set the buffer to a null string.
565 565 */
566 566 *(destination->text.p) = '\0';
567 567 }
568 568
569 569 status = pclose(fd);
570 570 if (status != 0) {
571 571 WCSTOMBS(mbs_buffer, command->buffer.start);
572 572 fatal_mksh(gettext("The command `%s' returned status `%d'"),
573 573 mbs_buffer,
574 574 WEXITSTATUS(status));
575 575 }
576 576 }
577 577
578 578
↓ open down ↓ |
192 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX