4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Copyright 2015, Joyent, Inc.
34 * Execute one commandline
40 #include <sys/wait.h> /* WIFEXITED(status) */
41 #include <alloca.h> /* alloca() */
43 #include <stdio.h> /* errno */
44 #include <errno.h> /* errno */
45 #include <fcntl.h> /* open() */
46 #include <mksh/dosys.h>
47 #include <mksh/macro.h> /* getvar() */
48 #include <mksh/misc.h> /* getmem(), fatal_mksh(), errmsg() */
49 #include <sys/signal.h> /* SIG_DFL */
50 #include <sys/stat.h> /* open() */
51 #include <sys/wait.h> /* wait() */
52 #include <ulimit.h> /* ulimit() */
53 #include <unistd.h> /* close(), dup2() */
54 #include <stdlib.h> /* closefrom() */
66 * File table of contents
68 static Boolean
exec_vp(register char *name
, register char **argv
, char **envp
, register Boolean ignore_error
, pathpt vroot_path
);
71 * Workaround for NFS bug. Sometimes, when running 'open' on a remote
72 * dmake server, it fails with "Stale NFS file handle" error.
73 * The second attempt seems to work.
76 my_open(const char *path
, int oflag
, mode_t mode
) {
77 int res
= open(path
, oflag
, mode
);
78 if (res
< 0 && (errno
== ESTALE
|| errno
== EAGAIN
)) {
79 /* Stale NFS file handle. Try again */
80 res
= open(path
, oflag
, mode
);
87 * redirect_io(char *stdout_file, char *stderr_file)
89 * Redirects stdout and stderr for a child mksh process.
92 redirect_io(char *stdout_file
, char *stderr_file
)
97 if ((i
= my_open(stdout_file
,
98 O_WRONLY
| O_CREAT
| O_TRUNC
| O_DSYNC
,
99 S_IREAD
| S_IWRITE
)) < 0) {
100 fatal_mksh(gettext("Couldn't open standard out temp file `%s': %s"),
104 if (dup2(i
, 1) == -1) {
105 fatal_mksh("*** Error: dup2(3, 1) failed: %s",
110 if (stderr_file
== NULL
) {
111 if (dup2(1, 2) == -1) {
112 fatal_mksh("*** Error: dup2(1, 2) failed: %s",
115 } else if ((i
= my_open(stderr_file
,
116 O_WRONLY
| O_CREAT
| O_TRUNC
| O_DSYNC
,
117 S_IREAD
| S_IWRITE
)) < 0) {
118 fatal_mksh(gettext("Couldn't open standard error temp file `%s': %s"),
122 if (dup2(i
, 2) == -1) {
123 fatal_mksh("*** Error: dup2(3, 2) failed: %s",
131 * doshell(command, ignore_error)
133 * Used to run command lines that include shell meta-characters.
134 * The make macro SHELL is supposed to contain a path to the shell.
137 * The pid of the process we started
140 * command The command to run
141 * ignore_error Should we abort on error?
143 * Global variables used:
144 * filter_stderr If -X is on we redirect stderr
145 * shell_name The Name "SHELL", used to get the path to shell
148 doshell(wchar_t *command
, register Boolean ignore_error
, char *stdout_file
, char *stderr_file
, int nice_prio
)
154 char nice_prio_buf
[MAXPATHLEN
];
155 register Name shell
= getvar(shell_name
);
156 register char *shellname
;
157 char *tmp_mbs_buffer
;
160 if (IS_EQUAL(shell
->string_mb
, "")) {
163 if ((shellname
= strrchr(shell
->string_mb
, (int) slash_char
)) == NULL
) {
164 shellname
= shell
->string_mb
;
170 * Only prepend the /usr/bin/nice command to the original command
171 * if the nice priority, nice_prio, is NOT zero (0).
172 * Nice priorities can be a positive or a negative number.
174 if (nice_prio
!= 0) {
175 argv
[argv_index
++] = (char *)"nice";
176 (void) sprintf(nice_prio_buf
, "-%d", nice_prio
);
177 argv
[argv_index
++] = strdup(nice_prio_buf
);
179 argv
[argv_index
++] = shellname
;
180 argv
[argv_index
++] = (char*)(ignore_error
? "-c" : "-ce");
181 if ((length
= wcslen(command
)) >= MAXPATHLEN
) {
182 tmp_mbs_buffer
= getmem((length
* MB_LEN_MAX
) + 1);
183 (void) wcstombs(tmp_mbs_buffer
, command
, (length
* MB_LEN_MAX
) + 1);
184 cmd_argv_index
= argv_index
;
185 argv
[argv_index
++] = strdup(tmp_mbs_buffer
);
186 retmem_mb(tmp_mbs_buffer
);
188 WCSTOMBS(mbs_buffer
, command
);
189 cmd_argv_index
= argv_index
;
190 argv
[argv_index
++] = strdup(mbs_buffer
);
192 argv
[argv_index
] = NULL
;
193 (void) fflush(stdout
);
194 if ((childPid
= fork()) == 0) {
195 enable_interrupt((void (*) (int)) SIG_DFL
);
201 if (nice_prio
!= 0) {
202 (void) execve("/usr/bin/nice", argv
, environ
);
203 fatal_mksh(gettext("Could not load `/usr/bin/nice': %s"),
206 (void) execve(shell
->string_mb
, argv
, environ
);
207 fatal_mksh(gettext("Could not load Shell from `%s': %s"),
212 if (childPid
== -1) {
213 fatal_mksh(gettext("fork failed: %s"),
216 retmem_mb(argv
[cmd_argv_index
]);
221 * exec_vp(name, argv, envp, ignore_error)
223 * Like execve, but does path search.
224 * This starts command when make invokes it directly (without a shell).
227 * Returns false if the exec failed
230 * name The name of the command to run
231 * argv Arguments for the command
232 * envp The environment for it
233 * ignore_error Should we abort on error?
235 * Global variables used:
236 * shell_name The Name "SHELL", used to get the path to shell
237 * vroot_path The path used by the vroot package
240 exec_vp(register char *name
, register char **argv
, char **envp
, register Boolean ignore_error
, pathpt vroot_path
)
242 register Name shell
= getvar(shell_name
);
243 register char *shellname
;
247 if (IS_EQUAL(shell
->string_mb
, "")) {
251 for (int i
= 0; i
< 5; i
++) {
252 (void) execve_vroot(name
,
260 /* That failed. Let the shell handle it */
261 shellname
= strrchr(shell
->string_mb
, (int) slash_char
);
262 if (shellname
== NULL
) {
263 shellname
= shell
->string_mb
;
267 shargv
[0] = shellname
;
268 shargv
[1] = (char*)(ignore_error
? "-c" : "-ce");
271 tmp_shell
= getvar(shell_name
);
272 if (IS_EQUAL(tmp_shell
->string_mb
, "")) {
273 tmp_shell
= shell_name
;
275 (void) execve_vroot(tmp_shell
->string_mb
,
283 * The program is busy (debugged?).
284 * Wait and then try again.
286 (void) sleep((unsigned) i
);
297 * doexec(command, ignore_error)
299 * Will scan an argument string and split it into words
300 * thus building an argument list that can be passed to exec_ve()
303 * The pid of the process started here
306 * command The command to run
307 * ignore_error Should we abort on error?
309 * Global variables used:
310 * filter_stderr If -X is on we redirect stderr
313 doexec(register wchar_t *command
, register Boolean ignore_error
, char *stdout_file
, char *stderr_file
, pathpt vroot_path
, int nice_prio
)
318 char nice_prio_buf
[MAXPATHLEN
];
322 char *tmp_mbs_buffer
;
325 * Only prepend the /usr/bin/nice command to the original command
326 * if the nice priority, nice_prio, is NOT zero (0).
327 * Nice priorities can be a positive or a negative number.
329 if (nice_prio
!= 0) {
332 for (t
= command
; *t
!= (int) nul_char
; t
++) {
337 argv
= (char **)alloca(arg_count
* (sizeof(char *)));
339 * Reserve argv[0] for sh in case of exec_vp failure.
340 * Don't worry about prepending /usr/bin/nice command to argv[0].
341 * In fact, doing it may cause the sh command to fail!
344 if ((length
= wcslen(command
)) >= MAXPATHLEN
) {
345 tmp_mbs_buffer
= getmem((length
* MB_LEN_MAX
) + 1);
346 (void) wcstombs(tmp_mbs_buffer
, command
, (length
* MB_LEN_MAX
) + 1);
347 argv
[0] = strdup(tmp_mbs_buffer
);
348 retmem_mb(tmp_mbs_buffer
);
350 WCSTOMBS(mbs_buffer
, command
);
351 argv
[0] = strdup(mbs_buffer
);
354 if (nice_prio
!= 0) {
355 *p
++ = strdup("/usr/bin/nice");
356 (void) sprintf(nice_prio_buf
, "-%d", nice_prio
);
357 *p
++ = strdup(nice_prio_buf
);
359 /* Build list of argument words. */
360 for (t
= command
; *t
;) {
361 if (p
>= &argv
[arg_count
]) {
362 /* This should never happen, right? */
363 WCSTOMBS(mbs_buffer
, command
);
364 fatal_mksh(gettext("Command `%s' has more than %d arguments"),
369 while (!iswspace(*t
) && (*t
!= (int) nul_char
)) {
373 for (*t
++ = (int) nul_char
; iswspace(*t
); t
++);
375 if ((length
= wcslen(q
)) >= MAXPATHLEN
) {
376 tmp_mbs_buffer
= getmem((length
* MB_LEN_MAX
) + 1);
377 (void) wcstombs(tmp_mbs_buffer
, q
, (length
* MB_LEN_MAX
) + 1);
378 *p
++ = strdup(tmp_mbs_buffer
);
379 retmem_mb(tmp_mbs_buffer
);
381 WCSTOMBS(mbs_buffer
, q
);
382 *p
++ = strdup(mbs_buffer
);
387 /* Then exec the command with that argument list. */
388 (void) fflush(stdout
);
389 if ((childPid
= fork()) == 0) {
390 enable_interrupt((void (*) (int)) SIG_DFL
);
396 (void) exec_vp(argv
[1], argv
, environ
, ignore_error
, vroot_path
);
397 fatal_mksh(gettext("Cannot load command `%s': %s"), argv
[1], errmsg(errno
));
399 if (childPid
== -1) {
400 fatal_mksh(gettext("fork failed: %s"),
403 for (int i
= 0; argv
[i
] != NULL
; i
++) {
410 * await(ignore_error, silent_error, target, command, running_pid)
412 * Wait for one child process and analyzes
413 * the returned status when the child process terminates.
416 * Returns true if commands ran OK
419 * ignore_error Should we abort on error?
420 * silent_error Should error messages be suppressed for dmake?
421 * target The target we are building, for error msgs
422 * command The command we ran, for error msgs
423 * running_pid The pid of the process we are waiting for
425 * Static variables used:
426 * filter_file The fd for the filter file
427 * filter_file_name The name of the filter file
429 * Global variables used:
430 * filter_stderr Set if -X is on
433 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
)
441 struct stat stat_buff
;
442 int termination_signal
;
443 char tmp_buf
[MAXPATHLEN
];
445 while ((pid
= wait(&status
)) != running_pid
) {
447 fatal_mksh(gettext("wait() failed: %s"), errmsg(errno
));
450 (void) fflush(stdout
);
451 (void) fflush(stderr
);
455 #ifdef PRINT_EXIT_STATUS
456 warning_mksh("I'm in await(), and status is 0.");
462 #ifdef PRINT_EXIT_STATUS
463 warning_mksh("I'm in await(), and status is *NOT* 0.");
467 exit_status
= WEXITSTATUS(status
);
469 #ifdef PRINT_EXIT_STATUS
470 warning_mksh("I'm in await(), and exit_status is %d.", exit_status
);
473 termination_signal
= WTERMSIG(status
);
474 core_dumped
= WCOREDUMP(status
);
477 * If the child returned an error, we now try to print a
478 * nice message about it.
481 tmp_buf
[0] = (int) nul_char
;
483 if (exit_status
!= 0) {
484 (void) fprintf(stdout
,
485 gettext("*** Error code %d"),
488 (void) fprintf(stdout
,
489 gettext("*** Signal %d"),
492 (void) fprintf(stdout
,
493 gettext(" - core dumped"));
497 (void) fprintf(stdout
,
498 gettext(" (ignored)"));
500 (void) fprintf(stdout
, "\n");
501 (void) fflush(stdout
);
504 #ifdef PRINT_EXIT_STATUS
505 warning_mksh("I'm in await(), returning failed.");
512 * sh_command2string(command, destination)
514 * Run one sh command and capture the output from it.
519 * command The command to run
520 * destination Where to deposit the output from the command
522 * Static variables used:
524 * Global variables used:
527 sh_command2string(register String command
, register String destination
)
532 Boolean command_generated_output
= false;
534 command
->text
.p
= (int) nul_char
;
535 WCSTOMBS(mbs_buffer
, command
->buffer
.start
);
536 if ((fd
= popen(mbs_buffer
, "r")) == NULL
) {
537 WCSTOMBS(mbs_buffer
, command
->buffer
.start
);
538 fatal_mksh(gettext("Could not run command `%s' for :sh transformation"),
541 while ((chr
= getc(fd
)) != EOF
) {
542 if (chr
== (int) newline_char
) {
543 chr
= (int) space_char
;
545 command_generated_output
= true;
546 append_char(chr
, destination
);
550 * We don't want to keep the last LINE_FEED since usually
551 * the output of the 'sh:' command is used to evaluate
552 * some MACRO. ( /bin/sh and other shell add a line feed
553 * to the output so that the prompt appear in the right place.
556 if (command_generated_output
){
557 if ( *(destination
->text
.p
-1) == (int) space_char
) {
558 * (-- destination
->text
.p
) = '\0';
562 * If the command didn't generate any output,
563 * set the buffer to a null string.
565 *(destination
->text
.p
) = '\0';
570 WCSTOMBS(mbs_buffer
, command
->buffer
.start
);
571 fatal_mksh(gettext("The command `%s' returned status `%d'"),
573 WEXITSTATUS(status
));