From 03d8c88b8774d40f3ff2596d7658f84938923caa Mon Sep 17 00:00:00 2001 From: nobu Date: Mon, 16 Feb 2004 06:45:32 +0000 Subject: * configure.in: check functions, fork spawnv. * io.c (rb_io_s_popen): accept argv not only single command line. * process.c (rb_proc_exec_n): export. * process.c (rb_check_argv): check if arguments are safe to invoke. * process.c (rb_fork): retry to fork. * process.c (rb_spawn): spawn child process asynchronously. * process.c (rb_f_system): raise an exception if the command could not execute. * win32/win32.c (rb_w32_argv_size): count necessary size for joined arguments. * win32/win32.c (rb_w32_join_argv): join arguments with quoting. * win32/win32.c (rb_w32_pipe_exec, rb_w32_spawn, rb_w32_aspawn): accept program name adding to command line. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@5725 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- process.c | 410 ++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 237 insertions(+), 173 deletions(-) (limited to 'process.c') diff --git a/process.c b/process.c index 697b7c3a84..18c46c2659 100644 --- a/process.c +++ b/process.c @@ -23,6 +23,9 @@ #ifdef HAVE_UNISTD_H #include #endif +#ifdef HAVE_FCNTL_H +#include +#endif #ifdef __DJGPP__ #include #endif @@ -912,12 +915,16 @@ proc_exec_v(argv, prog) char **argv; char *prog; { + int err; + if (!prog) prog = argv[0]; security(prog); prog = dln_find_exe(prog, 0); - if (!prog) + if (!prog) { + errno = ENOENT; return -1; + } #if (defined(MSDOS) && !defined(DJGPP)) || defined(__human68k__) || defined(__EMX__) || defined(OS2) { @@ -958,26 +965,23 @@ proc_exec_v(argv, prog) #endif /* MSDOS or __human68k__ or __EMX__ */ before_exec(); execv(prog, argv); + err = errno; after_exec(); + errno = err; return -1; } -static int -proc_exec_n(argc, argv, progv) +int +rb_proc_exec_n(argc, argv, prog) int argc; VALUE *argv; - VALUE progv; + const char *prog; { - char *prog = 0; char **args; int i; - if (progv) { - prog = RSTRING(progv)->ptr; - } args = ALLOCA_N(char*, argc+1); for (i=0; iptr; } args[i] = 0; @@ -1000,22 +1004,21 @@ rb_proc_exec(str) #ifdef _WIN32 before_exec(); - do_spawn(P_OVERLAY, (char *)str); + rb_w32_spawn(P_OVERLAY, (char *)str, 0); after_exec(); #else for (s=str; *s; s++) { if (*s != ' ' && !ISALPHA(*s) && strchr("*?{}[]<>()~&|\\$;'`\"\n",*s)) { -#if defined(MSDOS) int status; +#if defined(MSDOS) before_exec(); status = system(str); after_exec(); if (status != -1) exit(status); -#else -#if defined(__human68k__) || defined(__CYGWIN32__) || defined(__EMX__) +#elif defined(__human68k__) || defined(__CYGWIN32__) || defined(__EMX__) char *shell = dln_find_exe("sh", 0); - int status = -1; + status = -1; before_exec(); if (shell) execl(shell, "sh", "-c", str, (char *) NULL); @@ -1027,8 +1030,9 @@ rb_proc_exec(str) #else before_exec(); execl("/bin/sh", "sh", "-c", str, (char *)NULL); + status = errno; after_exec(); -#endif + errno = status; #endif return -1; } @@ -1050,12 +1054,20 @@ rb_proc_exec(str) return -1; } -#if defined(__human68k__) || defined(__DJGPP__) || defined(_WIN32) +#if defined(_WIN32) +#define HAVE_SPAWNV 1 +#endif + +#if !defined(HAVE_FORK) && defined(HAVE_SPAWNV) static int proc_spawn_v(argv, prog) char **argv; char *prog; { +#if defined(_WIN32) + char *cmd = ALLOCA_N(char, rb_w32_argv_size(argv)); + return rb_w32_spawn(P_NOWAIT, rb_w32_join_argv(cmd, argv), prog); +#else char *extension; int status; @@ -1091,13 +1103,10 @@ proc_spawn_v(argv, prog) } #endif before_exec(); -#if defined(_WIN32) - status = do_aspawn(P_WAIT, prog, argv); -#else status = spawnv(P_WAIT, prog, argv); -#endif after_exec(); return status; +#endif } static int @@ -1111,29 +1120,25 @@ proc_spawn_n(argc, argv, prog) args = ALLOCA_N(char*, argc + 1); for (i = 0; i < argc; i++) { - SafeStringValue(argv[i]); args[i] = RSTRING(argv[i])->ptr; } - if (prog) - SafeStringValue(prog); args[i] = (char*) 0; if (args[0]) return proc_spawn_v(args, prog ? RSTRING(prog)->ptr : 0); return -1; } -#if !defined(_WIN32) +#if defined(_WIN32) +#define proc_spawn(str) rb_w32_spawn(P_NOWAIT, str, 0) +#else static int -proc_spawn(sv) - VALUE sv; -{ +proc_spawn(str) char *str; +{ char *s, *t; char **argv, **a; int status; - SafeStringValue(sv); - str = s = RSTRING(sv)->ptr; for (s = str; *s; s++) { if (*s != ' ' && !ISALPHA(*s) && strchr("*?{}[]<>()~&|\\$;'`\"\n",*s)) { char *shell = dln_find_exe("sh", 0); @@ -1156,6 +1161,35 @@ proc_spawn(sv) #endif #endif +VALUE +rb_check_argv(argc, argv) + int argc; + VALUE *argv; +{ + VALUE tmp, prog; + int i; + + if (argc == 0) { + rb_raise(rb_eArgError, "wrong number of arguments"); + } + + prog = 0; + tmp = rb_check_array_type(argv[0]); + if (!NIL_P(tmp)) { + if (RARRAY(tmp)->len != 2) { + rb_raise(rb_eArgError, "wrong first argument"); + } + prog = RARRAY(tmp)->ptr[0]; + SafeStringValue(prog); + argv[0] = RARRAY(tmp)->ptr[1]; + } + for (i = 0; i < argc; i++) { + SafeStringValue(argv[i]); + } + security(RSTRING(prog ? prog : argv[0])->ptr); + return prog; +} + /* * call-seq: * exec(command [, arg, ...]) @@ -1186,35 +1220,116 @@ rb_f_exec(argc, argv) int argc; VALUE *argv; { - VALUE prog = 0; - VALUE tmp; + struct rb_exec_arg e; + VALUE prog; - if (argc == 0) { - rb_raise(rb_eArgError, "wrong number of arguments"); + prog = rb_check_argv(argc, argv); + if (!prog && argc == 1) { + --argc; + prog = *argv++; } + e.argc = argc; + e.argv = argv; + e.prog = prog ? RSTRING(prog)->ptr : 0; + rb_exec(&e); + rb_sys_fail(RSTRING(argv[0])->ptr); + return Qnil; /* dummy */ +} - tmp = rb_check_array_type(argv[0]); - if (!NIL_P(tmp)) { - if (RARRAY(tmp)->len != 2) { - rb_raise(rb_eArgError, "wrong first argument"); - } - prog = RARRAY(tmp)->ptr[0]; - SafeStringValue(prog); - argv[0] = RARRAY(tmp)->ptr[1]; - } - if (argc == 1 && prog == 0) { - VALUE cmd = argv[0]; +int +rb_exec(e) + const struct rb_exec_arg *e; +{ + int argc = e->argc; + VALUE *argv = e->argv; + const char *prog = e->prog; - SafeStringValue(cmd); - rb_proc_exec(RSTRING(cmd)->ptr); + if (argc == 0) { + rb_proc_exec(prog); } else { - proc_exec_n(argc, argv, prog); + rb_proc_exec_n(argc, argv, prog); } - rb_sys_fail(RSTRING(argv[0])->ptr); - return Qnil; /* dummy */ + return errno; } +#ifdef HAVE_FORK +int +rb_fork(status, chfunc, charg) + int *status; + int (*chfunc) _((void *)); + void *charg; +{ + int pid, err, state = 0, ep[2]; + +#ifndef __VMS + fflush(stdout); + fflush(stderr); +#endif + +#ifdef FD_CLOEXEC + if (chfunc) { + if (pipe(ep)) return -1; + if (fcntl(ep[0], F_SETFD, FD_CLOEXEC) || + fcntl(ep[1], F_SETFD, FD_CLOEXEC)) { + err = errno; + close(ep[0]); + close(ep[1]); + errno = err; + return -1; + } + } +#endif + while ((pid = fork()) < 0) { + switch (errno) { + case EAGAIN: +#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + if (!status && !chfunc) { + rb_thread_sleep(1); + continue; + } + else { + rb_protect((VALUE (*)())rb_thread_sleep, 1, &state); + if (status) *status = state; + if (!state) continue; + } + default: +#ifdef FD_CLOEXEC + if (chfunc) { + err = errno; + close(ep[0]); + close(ep[1]); + errno = err; + } +#endif + if (state && !status) rb_jump_tag(state); + return -1; + } + } + if (!pid) { + if (chfunc) { + err = (*chfunc)(charg); + write(ep[1], &err, sizeof(err)); + _exit(127); + } + } + else if (chfunc) { + close(ep[1]); + if ((state = read(ep[0], &err, sizeof(err))) < 0) { + err = errno; + } + close(ep[0]); + if (state) { + rb_syswait(pid); + errno = err; + return -1; + } + } + return pid; +} +#endif /* * call-seq: @@ -1227,11 +1342,12 @@ static VALUE rb_f_fork(obj) VALUE obj; { -#if !defined(__human68k__) && !defined(_WIN32) && !defined(__MACOS__) && !defined(__EMX__) && !defined(__VMS) +#ifdef HAVE_FORK int pid; rb_secure(2); - switch (pid = fork()) { + + switch (pid = rb_fork(0, 0, 0)) { case 0: #ifdef linux after_exec(); @@ -1337,6 +1453,48 @@ rb_syswait(pid) } } +int +rb_spawn(argc, argv) + int argc; + VALUE *argv; +{ + int status; + VALUE prog; +#if defined HAVE_FORK + int pid; + struct rb_exec_arg earg; +#endif + + prog = rb_check_argv(argc, argv); + + if (!prog && argc == 1) { + --argc; + prog = *argv++; + } +#if defined HAVE_FORK + earg.argc = argc; + earg.argv = argv; + earg.prog = prog ? RSTRING(prog)->ptr : 0; + status = rb_fork(&status, (int (*)_((void*)))rb_exec, &earg); +#elif defined HAVE_SPAWNV + if (!argc) { + status = proc_spawn(RSTRING(prog)->ptr); + } + else { + status = proc_spawn_n(argc, argv, prog); + } +#else + prog = rb_ary_join(rb_ary_new4(argc, argv), rb_str_new2(" ")); + status = system(StringValuePtr(prog)); +# if defined(__human68k__) || defined(__DJGPP__) + last_status_set(status == -1 ? 127 : status, 0); +# else + last_status_set((status & 0xff) << 8, 0); +# endif +#endif + return status; +} + /* * call-seq: * system(cmd [, arg, ...]) => true or false @@ -1362,135 +1520,39 @@ rb_f_system(argc, argv) VALUE *argv; { int status; -#if defined(__EMX__) - VALUE cmd; - - fflush(stdout); - fflush(stderr); - if (argc == 0) { - rb_last_status = Qnil; - rb_raise(rb_eArgError, "wrong number of arguments"); - } - - if (TYPE(argv[0]) == T_ARRAY) { - if (RARRAY(argv[0])->len != 2) { - rb_raise(rb_eArgError, "wrong first argument"); - } - argv[0] = RARRAY(argv[0])->ptr[0]; - } - cmd = rb_ary_join(rb_ary_new4(argc, argv), rb_str_new2(" ")); - - SafeStringValue(cmd); - status = do_spawn(RSTRING(cmd)->ptr); - last_status_set(status, 0); -#elif defined(__human68k__) || defined(__DJGPP__) || defined(_WIN32) - volatile VALUE prog = 0; - - fflush(stdout); - fflush(stderr); - if (argc == 0) { - rb_last_status = Qnil; - rb_raise(rb_eArgError, "wrong number of arguments"); - } - if (TYPE(argv[0]) == T_ARRAY) { - if (RARRAY(argv[0])->len != 2) { - rb_raise(rb_eArgError, "wrong first argument"); - } - prog = RARRAY(argv[0])->ptr[0]; - argv[0] = RARRAY(argv[0])->ptr[1]; - } - - if (argc == 1 && prog == 0) { -#if defined(_WIN32) - SafeStringValue(argv[0]); - status = do_spawn(P_WAIT, RSTRING(argv[0])->ptr); -#else - status = proc_spawn(argv[0]); -#endif - } - else { - status = proc_spawn_n(argc, argv, prog); - } -#if defined(_WIN32) - last_status_set(status, 0); -#else - last_status_set(status == -1 ? 127 : status, 0); + status = rb_spawn(argc, argv); + if (status == -1) rb_sys_fail(RSTRING(argv[0])->ptr); +#if defined(HAVE_FORK) || defined(HAVE_SPAWNV) + rb_syswait(status); + status = NUM2INT(rb_last_status); #endif -#elif defined(__VMS) - VALUE cmd; - - if (argc == 0) { - rb_last_status = Qnil; - rb_raise(rb_eArgError, "wrong number of arguments"); - } + if (status == EXIT_SUCCESS) return Qtrue; + return Qfalse; +} - if (TYPE(argv[0]) == T_ARRAY) { - if (RARRAY(argv[0])->len != 2) { - rb_raise(rb_eArgError, "wrong first argument"); - } - argv[0] = RARRAY(argv[0])->ptr[0]; - } - cmd = rb_ary_join(rb_ary_new4(argc, argv), rb_str_new2(" ")); +/* + * call-seq: + * spawn(cmd [, arg, ...]) => pid + * + * Similar to Kernel::system except for not waiting for + * end of _cmd_, but returns its pid. + */ - SafeStringValue(cmd); - status = system(RSTRING(cmd)->ptr); - last_status_set((status & 0xff) << 8, 0); -#else - volatile VALUE prog = 0; +static VALUE +rb_f_spawn(argc, argv) + int argc; + VALUE *argv; +{ int pid; - int i; - fflush(stdout); - fflush(stderr); - if (argc == 0) { - rb_last_status = Qnil; - rb_raise(rb_eArgError, "wrong number of arguments"); - } - - if (TYPE(argv[0]) == T_ARRAY) { - if (RARRAY(argv[0])->len != 2) { - rb_raise(rb_eArgError, "wrong first argument"); - } - prog = RARRAY(argv[0])->ptr[0]; - argv[0] = RARRAY(argv[0])->ptr[1]; - } - - if (prog) { - SafeStringValue(prog); - } - for (i = 0; i < argc; i++) { - SafeStringValue(argv[i]); - } - retry: - switch (pid = fork()) { - case 0: - if (argc == 1 && prog == 0) { - rb_proc_exec(RSTRING(argv[0])->ptr); - } - else { - proc_exec_n(argc, argv, prog); - } - _exit(127); - break; /* not reached */ - - case -1: - if (errno == EAGAIN) { - rb_thread_sleep(1); - goto retry; - } - rb_sys_fail(0); - break; - - default: - rb_syswait(pid); - } - - status = NUM2INT(rb_last_status); + pid = rb_spawn(argc, argv); + if (pid == -1) rb_sys_fail(RSTRING(argv[0])->ptr); +#if defined(HAVE_FORK) || defined(HAVE_SPAWNV) + return INT2NUM(pid); +#else + return Qnil; #endif - - if (status == EXIT_SUCCESS) return Qtrue; - return Qfalse; } /* @@ -3314,6 +3376,7 @@ Init_process() rb_define_global_function("fork", rb_f_fork, 0); rb_define_global_function("exit!", rb_f_exit_bang, -1); rb_define_global_function("system", rb_f_system, -1); + rb_define_global_function("spawn", rb_f_spawn, -1); rb_define_global_function("sleep", rb_f_sleep, -1); rb_mProcess = rb_define_module("Process"); @@ -3332,6 +3395,7 @@ Init_process() #endif rb_define_singleton_method(rb_mProcess, "fork", rb_f_fork, 0); + rb_define_singleton_method(rb_mProcess, "spawn", rb_f_spawn, -1); rb_define_singleton_method(rb_mProcess, "exit!", rb_f_exit_bang, -1); rb_define_singleton_method(rb_mProcess, "exit", rb_f_exit, -1); /* in eval.c */ rb_define_singleton_method(rb_mProcess, "abort", rb_f_abort, -1); /* in eval.c */ -- cgit v1.2.3