1 /* $NetBSD: run.c,v 1.64 2007/10/09 18:43:26 martin Exp $ */
4 * Copyright 1997 Piermont Information Systems Inc.
7 * Written by Philip A. Nelson for Piermont Information Systems Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Piermont Information Systems Inc.
21 * 4. The name of Piermont Information Systems Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
25 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35 * THE POSSIBILITY OF SUCH DAMAGE.
39 /* run.c -- routines to interact with other programs. */
41 /* XXX write return codes ignored. XXX */
55 #include <sys/ioctl.h>
56 #include <sys/types.h>
61 #include "menu_defs.h"
67 #define Xsystem(y) printf ("%s\n", y), 0
69 #define Xsystem(y) system(y)
75 int log_flip (menudesc
*, void *);
76 static int script_flip (menudesc
*, void *);
80 menu_ent logmenu
[2] = {
81 { NULL
, OPT_NOMENU
, 0, log_flip
},
82 { NULL
, OPT_NOMENU
, 0, script_flip
} };
85 log_menu_label(menudesc
*m
, int opt
, void *arg
)
87 wprintw(m
->mw
, "%s: %s",
88 msg_string(opt
? MSG_Scripting
: MSG_Logging
),
89 msg_string((opt
? scripting
: logging
) ? MSG_On
: MSG_Off
));
97 menu_no
= new_menu(MSG_Logging_functions
, logmenu
, 2, -1, 12,
98 0, 20, MC_SCROLL
, NULL
, log_menu_label
, NULL
,
99 MSG_Pick_an_option
, NULL
);
102 (void)fprintf(stderr
, "Dynamic menu creation failed.\n");
104 (void)fprintf(logfp
, "Dynamic menu creation failed.\n");
107 process_menu(menu_no
, NULL
);
113 log_flip(menudesc
*m
, void *arg
)
120 fprintf(logfp
, "Log ended at: %s\n", asctime(localtime(&tloc
)));
124 logfp
= fopen("/tmp/sysinst.log", "a");
128 "Log started at: %s\n", asctime(localtime(&tloc
)));
131 msg_display(MSG_openfail
, "log file", strerror(errno
));
139 script_flip(menudesc
*m
, void *arg
)
144 if (scripting
== 1) {
145 scripting_fprintf(NULL
, "# Script ended at: %s\n", asctime(localtime(&tloc
)));
150 script
= fopen("/tmp/sysinst.sh", "w");
151 if (script
!= NULL
) {
153 scripting_fprintf(NULL
, "#!/bin/sh\n");
154 scripting_fprintf(NULL
, "# Script started at: %s\n",
155 asctime(localtime(&tloc
)));
158 msg_display(MSG_openfail
, "script file", strerror(errno
));
165 collect(int kind
, char **buffer
, const char *name
, ...)
167 size_t nbytes
; /* Number of bytes in buffer. */
168 size_t fbytes
; /* Number of bytes in file. */
169 struct stat st
; /* stat information. */
172 char fileorcmd
[STRSIZE
];
177 vsnprintf(fileorcmd
, sizeof fileorcmd
, name
, ap
);
180 if (kind
== T_FILE
) {
181 /* Get the file information. */
182 if (stat(fileorcmd
, &st
)) {
186 fbytes
= (size_t)st
.st_size
;
189 f
= fopen(fileorcmd
, "r");
195 /* Open the program. */
196 f
= popen(fileorcmd
, "r");
207 /* Allocate the buffer size. */
208 *buffer
= cp
= malloc(fbytes
+ 1);
212 /* Read the buffer. */
214 while (nbytes
< fbytes
&& (ch
= fgetc(f
)) != EOF
)
229 * system(3), but with a debug wrapper.
230 * use only for curses sub-applications.
233 do_system(const char *execstr
)
238 * The following may be more than one function call. Can't just
239 * "return Xsystem (command);"
242 ret
= Xsystem(execstr
);
248 make_argv(const char *cmd
)
255 struct dirent
*dirent
;
258 for (; *cmd
!= 0; cmd
= cp
+ strspn(cp
, " "), argc
++) {
260 cp
= strchr(++cmd
, '\'');
262 cp
= strchr(cmd
, ' ');
265 argv
= realloc(argv
, (argc
+ 2) * sizeof *argv
);
267 err(1, "realloc(argv) for %s", cmd
);
268 asprintf(argv
+ argc
, "%.*s", (int)(cp
- cmd
), cmd
);
269 /* Hack to remove %xx encoded ftp password */
270 dp
= strstr(cmd
, ":%");
271 if (dp
!= NULL
&& dp
< cp
) {
272 for (fn
= dp
+ 4; *fn
== '%'; fn
+= 3)
275 memset(dp
+ 1, '*', fn
- dp
- 1);
281 /* do limited filename globbing */
283 fn
= strrchr(dp
, '/');
294 while ((dirent
= readdir(dir
))) {
295 if (dirent
->d_name
[0] == '.')
297 if (strncmp(dirent
->d_name
, fn
, l
) != 0)
299 if (dp
!= argv
[argc
])
301 argv
= realloc(argv
, (argc
+ 2) * sizeof *argv
);
303 err(1, "realloc(argv) for %s", cmd
);
304 asprintf(argv
+ argc
, "%.*s%s", (int)(fn
- dp
), dp
,
307 if (dp
!= argv
[argc
])
316 free_argv(char **argv
)
320 for (n
= argv
; (a
= *n
++);)
326 show_cmd(const char *scmd
, struct winsize
*win
)
337 mvaddstr(0, 4, msg_string(MSG_Status
));
339 addstr(msg_string(MSG_Running
));
341 mvaddstr(1, 4, msg_string(MSG_Command
));
346 for (n
= win
->ws_col
; (m
= min(n
, 30)) > 0; n
-= m
)
347 addstr( "------------------------------" + 30 - m
);
350 nrow
= getcury(stdscr
) + 1;
352 actionwin
= subwin(stdscr
, win
->ws_row
- nrow
, win
->ws_col
, nrow
, 0);
353 if (actionwin
== NULL
) {
354 fprintf(stderr
, "sysinst: failed to allocate output window.\n");
357 scrollok(actionwin
, TRUE
);
359 wbkgd(actionwin
, getbkgd(stdscr
));
360 wattrset(actionwin
, getattrs(stdscr
));
363 wmove(actionwin
, 0, 0);
370 * launch a program inside a subwindow, and report its return status when done
373 launch_subwin(WINDOW
**actionwin
, char **args
, struct winsize
*win
, int flags
,
374 const char *scmd
, const char **errstr
)
378 int status
, master
, slave
;
379 fd_set active_fd_set
, read_fd_set
;
387 static int do_tioccons
= 2;
390 (void)tcgetattr(STDIN_FILENO
, &tt
);
391 if (openpty(&master
, &slave
, NULL
, &tt
, win
) == -1) {
392 *errstr
= "openpty() failed";
398 /* ignore tty signals until we're done with subprocess setup */
400 ioctl(master
, TIOCPKT
, &ttysig_ignore
);
402 /* Try to get console output into our pipe */
404 if (ioctl(slave
, TIOCCONS
, &do_tioccons
) == 0
405 && do_tioccons
== 2) {
406 /* test our output - we don't want it grabbed */
408 ioctl(master
, FIONREAD
, &do_tioccons
);
409 if (do_tioccons
!= 0) {
411 ioctl(slave
, TIOCCONS
, &do_tioccons
);
427 *errstr
= "fork() failed";
430 (void)close(STDIN_FILENO
);
431 /* silently stop curses */
432 (void)close(STDOUT_FILENO
);
433 (void)open("/dev/null", O_RDWR
, 0);
434 dup2(STDIN_FILENO
, STDOUT_FILENO
);
438 rtt
.c_lflag
|= (ICANON
|ECHO
);
439 (void)tcsetattr(slave
, TCSANOW
, &rtt
);
442 fprintf(logfp
, "executing: %s\n", scmd
);
446 fprintf(script
, "%s\n", scmd
);
449 if (strcmp(args
[0], "cd") == 0 && strcmp(args
[2], "&&") == 0) {
450 target_chdir_or_die(args
[1]);
453 if (flags
& RUN_XFER_DIR
)
454 target_chdir_or_die(xfer_dir
);
456 * If target_prefix == "", the chroot will fail, but
457 * that's ok, since we don't need it then.
459 if (flags
& RUN_CHROOT
&& *target_prefix()
460 && chroot(target_prefix()) != 0)
461 warn("chroot(%s) for %s", target_prefix(), *args
);
464 warn("execvp %s", *args
);
467 // break; /* end of child */
470 * parent: we've set up the subprocess.
471 * forward tty signals to its process group.
473 ttysig_forward
= child
;
479 * Now loop transferring program output to screen, and keyboard
480 * input to the program.
483 FD_ZERO(&active_fd_set
);
484 FD_SET(master
, &active_fd_set
);
485 FD_SET(STDIN_FILENO
, &active_fd_set
);
487 for (selectfailed
= 0;;) {
489 const char *mmsg
= "select(2) failed but no child died?";
491 (void)fprintf(logfp
, mmsg
);
494 read_fd_set
= active_fd_set
;
497 i
= select(FD_SETSIZE
, &read_fd_set
, NULL
, NULL
, &tmo
);
498 if (i
== 0 && *actionwin
== NULL
)
499 *actionwin
= show_cmd(scmd
, win
);
501 if (errno
!= EINTR
) {
505 "select failure: %s\n",
509 } else for (i
= 0; i
< FD_SETSIZE
; ++i
) {
510 if (!FD_ISSET(i
, &read_fd_set
))
512 n
= read(i
, ibuf
, sizeof ibuf
- 1);
520 if (i
== STDIN_FILENO
) {
521 (void)write(master
, ibuf
, (size_t)n
);
522 if (!(rtt
.c_lflag
& ECHO
))
527 if (pktdata
& TIOCPKT_IOCTL
)
528 memcpy(&rtt
, ibuf
, sizeof(rtt
));
533 if (*cp
== 0 || flags
& RUN_SILENT
)
536 fprintf(logfp
, "%s", cp
);
539 if (*actionwin
== NULL
)
540 *actionwin
= show_cmd(scmd
, win
);
541 /* posix curses is braindead wrt \r\n so... */
542 for (ncp
= cp
; (ncp
= strstr(ncp
, "\r\n")); ncp
+= 2) {
546 waddstr(*actionwin
, cp
);
547 wrefresh(*actionwin
);
549 pid
= wait4(child
, &status
, WNOHANG
, 0);
550 if (pid
== child
&& (WIFEXITED(status
) || WIFSIGNALED(status
)))
558 /* from here on out, we take tty signals ourselves */
563 if (WIFEXITED(status
)) {
564 *errstr
= msg_string(MSG_Command_failed
);
565 return WEXITSTATUS(status
);
567 if (WIFSIGNALED(status
)) {
568 *errstr
= msg_string(MSG_Command_ended_on_signal
);
569 return WTERMSIG(status
);
575 * generic program runner.
577 * RUN_DISPLAY display command name and output
578 * RUN_FATAL program errors are fatal
579 * RUN_CHROOT chroot to target before the exec
580 * RUN_FULLSCREEN display output only
581 * RUN_SILENT do not display program output
582 * RUN_ERROR_OK don't wait for key if program fails
583 * RUN_PROGRESS don't wait for key if program has output
584 * If both RUN_DISPLAY and RUN_SILENT are clear then the program name will
585 * be displayed as soon as it generates output.
586 * Steps are taken to collect console messages, they will be interleaved
587 * into the program output - but not upset curses.
591 run_program(int flags
, const char *cmd
, ...)
596 WINDOW
*actionwin
= NULL
;
599 const char *errstr
= NULL
;
602 vasprintf(&scmd
, cmd
, ap
);
605 err(1, "vasprintf(&scmd, \"%s\", ...)", cmd
);
607 args
= make_argv(scmd
);
609 /* Make curses save tty settings */
612 (void)ioctl(STDIN_FILENO
, TIOCGWINSZ
, &win
);
613 /* Apparently, we sometimes get 0x0 back, and that's not useful */
619 if ((flags
& RUN_DISPLAY
) != 0) {
620 if (flags
& RUN_FULLSCREEN
) {
627 actionwin
= show_cmd(scmd
, &win
);
631 ret
= launch_subwin(&actionwin
, args
, &win
, flags
, scmd
, &errstr
);
634 /* If the command failed, show command name */
635 if (actionwin
== NULL
&& ret
!= 0 && !(flags
& RUN_ERROR_OK
))
636 actionwin
= show_cmd(scmd
, &win
);
638 if (actionwin
!= NULL
) {
640 getyx(actionwin
, y
, x
);
641 if (actionwin
!= stdscr
)
642 mvaddstr(0, 4, msg_string(MSG_Status
));
644 if (actionwin
== stdscr
&& x
!= 0)
646 x
= 1; /* force newline below */
651 if (actionwin
!= stdscr
) {
653 addstr(msg_string(MSG_Finished
));
659 if ((ret
!= 0 && !(flags
& RUN_ERROR_OK
)) ||
660 (y
+ x
!= 0 && !(flags
& RUN_PROGRESS
))) {
661 if (actionwin
!= stdscr
)
662 move(getbegy(actionwin
) - 2, 5);
665 addstr(msg_string(MSG_Hit_enter_to_continue
));
670 /* give user 1 second to see messages */
677 /* restore tty setting we saved earlier */
680 /* clean things up */
681 if (actionwin
!= NULL
) {
682 if (actionwin
!= stdscr
)
684 if (err
== 0 || !(flags
& RUN_NO_CLEAR
)) {
695 if (ret
!= 0 && flags
& RUN_FATAL
)