static void check_modes(char *fname, int mode) { struct stat st; struct group *grp; struct passwd *pwd; char ch_cmd[200]; if (stat(fname, &st) == 0) { grp = getgrgid(st.st_gid); pwd = getpwuid(st.st_uid); if ( strcmp(pwd->pw_name, pikrellcam.effective_user) || strcmp(grp->gr_name, "www-data") ) { snprintf(ch_cmd, sizeof(ch_cmd), "sudo chown %s.www-data %s", pikrellcam.effective_user, fname); exec_wait(ch_cmd, NULL); } if ((st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) != mode) { snprintf(ch_cmd, sizeof(ch_cmd), "sudo chmod %o %s", mode, fname); exec_wait(ch_cmd, NULL); } } }
void event_still_capture_cmd(char *cmd) { if (!cmd || !*cmd) return; exec_wait(cmd, NULL); }
bool promote_dependencies(const std::string& snapshot, std::vector<std::string>& dependencies) { zfs_elevate(); std::cout << "Searching for origin " << snapshot << std::endl; std::string snap_data; int rc = exec_wait(find_zfs_cmd(), snap_data, "list", "-H", "-o", "name", NULL); if(rc!=0) return false; std::vector<std::string> snaps; Tokenize(snap_data, snaps, "\n"); std::string snap_folder = ExtractFilePath(snapshot); for(size_t i=0;i<snaps.size();++i) { if( !next(trim(snaps[i]), 0, snap_folder) || trim(snaps[i]).size()<=snap_folder.size() ) continue; std::string stdout; std::string subvolume_folder = snaps[i]; int rc=exec_wait(find_zfs_cmd(), stdout, "get", "-H", "-o", "value", "origin", subvolume_folder.c_str(), NULL); if(rc==0) { stdout=trim(stdout); if(stdout==snapshot) { std::cout << "Origin is " << subvolume_folder << std::endl; if(exec_wait(find_zfs_cmd(), true, "promote", subvolume_folder.c_str(), NULL)!=0) { return false; } dependencies.push_back(subvolume_folder); } } } return true; }
bool make_readonly(int mode, std::string subvolume_folder) { #ifdef _WIN32 return false; #else if(mode==mode_btrfs) { int rc=exec_wait(find_btrfs_cmd(), true, "property", "set", "-ts", subvolume_folder.c_str(), "ro", "true", NULL); return rc==0; } else if(mode==mode_zfs) { zfs_elevate(); int rc=exec_wait(find_zfs_cmd(), true, "snapshot", (subvolume_folder+"@ro").c_str(), NULL); return rc==0; } return false; #endif }
bool is_subvolume(int mode, std::string subvolume_folder) { #ifdef _WIN32 return true; #else if(mode==mode_btrfs) { int rc=exec_wait(find_btrfs_cmd(), false, "subvolume", "list", subvolume_folder.c_str(), NULL); return rc==0; } else if(mode==mode_zfs) { zfs_elevate(); int rc=exec_wait(find_zfs_cmd(), false, "list", subvolume_folder.c_str(), NULL); return rc==0; } return false; #endif }
/* Useful for emailing a motion event preview jpeg. */ void event_preview_save_cmd(char *cmd) { if (!cmd || !*cmd) return; // log_printf("event_preview_save_cmd(); running %s %s\n", cmd, // pikrellcam.preview_filename); exec_wait(cmd, pikrellcam.preview_filename); }
/* Generate a motion area thumb. */ void event_motion_area_thumb(void) { char *cmd = NULL; asprintf(&cmd, "%s/scripts-dist/_thumb $F $m $P $G $i $J $K $Y", pikrellcam.install_dir); exec_wait(cmd, pikrellcam.preview_filename); if (cmd) free(cmd); }
void log_lines(void) { char buf[128]; if (pikrellcam.log_lines > 0) { snprintf(buf, sizeof(buf), "%s/scripts-dist/_log-lines %d $G", pikrellcam.install_dir, pikrellcam.log_lines); exec_wait(buf, NULL); } }
bool create_snapshot(int mode, std::string snapshot_src, std::string snapshot_dst) { #ifdef _WIN32 return CopyFolder(widen(snapshot_src), widen(snapshot_dst)); #else if(mode==mode_btrfs) { int rc=exec_wait(find_btrfs_cmd(), true, "subvolume", "snapshot", snapshot_src.c_str(), snapshot_dst.c_str(), NULL); chown_dir(snapshot_dst); return rc==0; } else if(mode==mode_zfs) { zfs_elevate(); int rc=exec_wait(find_zfs_cmd(), true, "clone", (snapshot_src+"@ro").c_str(), snapshot_dst.c_str(), NULL); chown_dir(snapshot_dst); return rc==0; } return false; #endif }
bool create_subvolume(int mode, std::string subvolume_folder) { #ifdef _WIN32 return os_create_dir(subvolume_folder); #else if(mode==mode_btrfs) { int rc=exec_wait(find_btrfs_cmd(), true, "subvolume", "create", subvolume_folder.c_str(), NULL); chown_dir(subvolume_folder); return rc==0; } else if(mode==mode_zfs) { zfs_elevate(); int rc=exec_wait(find_zfs_cmd(), true, "create", "-p", subvolume_folder.c_str(), NULL); chown_dir(subvolume_folder); return rc==0; } return false; #endif }
std::string find_zfs_cmd() { static std::string zfs_cmd; if(!zfs_cmd.empty()) { return zfs_cmd; } if(exec_wait("zfs", false, "--version", NULL)==2) { zfs_cmd="zfs"; return zfs_cmd; } else if(exec_wait("/sbin/zfs", false, "--version", NULL)==2) { zfs_cmd="/sbin/zfs"; return zfs_cmd; } else if(exec_wait("/bin/zfs", false, "--version", NULL)==2) { zfs_cmd="/bin/zfs"; return zfs_cmd; } else if(exec_wait("/usr/sbin/zfs", false, "--version", NULL)==2) { zfs_cmd="/usr/sbin/zfs"; return zfs_cmd; } else if(exec_wait("/usr/bin/zfs", false, "--version", NULL)==2) { zfs_cmd="/usr/bin/zfs"; return zfs_cmd; } else { zfs_cmd="zfs"; return zfs_cmd; } }
std::string find_btrfs_cmd() { static std::string btrfs_cmd; if(!btrfs_cmd.empty()) { return btrfs_cmd; } if(exec_wait("btrfs", false, "--version", NULL)==0) { btrfs_cmd="btrfs"; return btrfs_cmd; } else if(exec_wait("/sbin/btrfs", false, "--version", NULL)==0) { btrfs_cmd="/sbin/btrfs"; return btrfs_cmd; } else if(exec_wait("/bin/btrfs", false, "--version", NULL)==0) { btrfs_cmd="/bin/btrfs"; return btrfs_cmd; } else if(exec_wait("/usr/sbin/btrfs", false, "--version", NULL)==0) { btrfs_cmd="/usr/sbin/btrfs"; return btrfs_cmd; } else if(exec_wait("/usr/bin/btrfs", false, "--version", NULL)==0) { btrfs_cmd="/usr/bin/btrfs"; return btrfs_cmd; } else { btrfs_cmd="btrfs"; return btrfs_cmd; } }
static boolean make_dir(char *dir) { boolean dir_exists; if ((dir_exists = isdir(dir)) == FALSE) { exec_wait("sudo mkdir -p $F", dir); if ((dir_exists = isdir(dir)) == FALSE) log_printf("Make directory failed: %s\n", dir); else dir_exists = TRUE; } if (dir_exists) check_modes(dir, 0775); return dir_exists; }
int make1( TARGET * const t ) { state * pState; memset( (char *)counts, 0, sizeof( *counts ) ); /* Recursively make the target and its dependencies. */ push_state( &state_stack, t, NULL, T_STATE_MAKE1A ); while ( 1 ) { while ( ( pState = current_state( &state_stack ) ) ) { if ( intr ) pop_state( &state_stack ); switch ( pState->curstate ) { case T_STATE_MAKE1A: make1a( pState ); break; case T_STATE_MAKE1B: make1b( pState ); break; case T_STATE_MAKE1C: make1c( pState ); break; default: assert( !"make1(): Invalid state detected." ); } } if ( !cmdsrunning ) break; /* Wait for outstanding commands to finish running. */ exec_wait(); } clear_state_freelist(); /* Talk about it. */ if ( counts->failed ) printf( "...failed updating %d target%s...\n", counts->failed, counts->failed > 1 ? "s" : "" ); if ( DEBUG_MAKE && counts->skipped ) printf( "...skipped %d target%s...\n", counts->skipped, counts->skipped > 1 ? "s" : "" ); if ( DEBUG_MAKE && counts->made ) printf( "...updated %d target%s...\n", counts->made, counts->made > 1 ? "s" : "" ); return counts->total != counts->made; }
bool get_mountpoint(int mode, std::string subvolume_folder) { #ifdef _WIN32 std::cout << subvolume_folder << std::endl; return true; #else if(mode==mode_btrfs) { std::cout << subvolume_folder << std::endl; return true; } else if(mode==mode_zfs) { zfs_elevate(); int rc=exec_wait(find_zfs_cmd(), true, "get", "-H", "-o", "value", "mountpoint", subvolume_folder.c_str(), NULL); return rc==0; } return false; #endif }
int built_cmd (CMD *cmd) { int status = SUCCESS; if (!cmd) return status; if(cmd->fromType != NONE) { //setup input redirection int red_err = set_red_in(cmd); if(red_err != SUCCESS) { perror("RED_IN: "); exit(red_err); } } if(cmd->toType != NONE) { //setup output redirection int red_err = set_red_out(cmd); if (red_err != SUCCESS) { perror("RED_OUT: "); exit(red_err); } } if (strcmp(cmd->argv[0], "dirs") == 0) status = exec_dirs(); else if (strcmp(cmd->argv[0], "cd") == 0) status = exec_cd(cmd); else if (strcmp(cmd->argv[0], "wait") == 0) status = exec_wait(); return status; }
int make1( LIST * targets ) { state * pState; int status = 0; memset( (char *)counts, 0, sizeof( *counts ) ); { LISTITER iter, end; stack temp_stack = { NULL }; for ( iter = list_begin( targets ), end = list_end( targets ); iter != end; iter = list_next( iter ) ) push_state( &temp_stack, bindtarget( list_item( iter ) ), NULL, T_STATE_MAKE1A ); push_stack_on_stack( &state_stack, &temp_stack ); } /* Clear any state left over from the past */ quit = 0; /* Recursively make the target and its dependencies. */ while ( 1 ) { while ( ( pState = current_state( &state_stack ) ) ) { if ( quit ) pop_state( &state_stack ); switch ( pState->curstate ) { case T_STATE_MAKE1A: make1a( pState ); break; case T_STATE_MAKE1B: make1b( pState ); break; case T_STATE_MAKE1C: make1c( pState ); break; default: assert( !"make1(): Invalid state detected." ); } } if ( !cmdsrunning ) break; /* Wait for outstanding commands to finish running. */ exec_wait(); } clear_state_freelist(); /* Talk about it. */ if ( counts->failed ) printf( "...failed updating %d target%s...\n", counts->failed, counts->failed > 1 ? "s" : "" ); if ( DEBUG_MAKE && counts->skipped ) printf( "...skipped %d target%s...\n", counts->skipped, counts->skipped > 1 ? "s" : "" ); if ( DEBUG_MAKE && counts->made ) printf( "...updated %d target%s...\n", counts->made, counts->made > 1 ? "s" : "" ); /* If we were interrupted, exit now that all child processes have finished. */ if ( intr ) exit( 1 ); { LISTITER iter, end; for ( iter = list_begin( targets ), end = list_end( targets ); iter != end; iter = list_next( iter ) ) { /* Check that the target was updated and that the update succeeded. */ TARGET * t = bindtarget( list_item( iter ) ); if (t->progress == T_MAKE_DONE) { if (t->status != EXEC_CMD_OK) status = 1; } else if ( ! ( t->progress == T_MAKE_NOEXEC_DONE && globs.noexec ) ) { status = 1; } } } return status; }
bool remove_subvolume(int mode, std::string subvolume_folder, bool quiet=false) { #ifdef _WIN32 return os_remove_nonempty_dir(widen(subvolume_folder)); #else if(mode==mode_btrfs) { int compat_rc = exec_wait(find_btrfs_cmd(), false, "subvolume", "delete", "-c", NULL); if(compat_rc==1) { compat_rc = exec_wait("/bin/sh", false, "-c", (find_btrfs_cmd() + " subvolume delete -c 2>&1 | grep \"ERROR: error accessing '-c'\"").c_str(), NULL); if(compat_rc==0) { compat_rc=12; } } int rc; if(compat_rc==12) { rc=exec_wait(find_btrfs_cmd(), !quiet, "subvolume", "delete", subvolume_folder.c_str(), NULL); } else { rc=exec_wait(find_btrfs_cmd(), !quiet, "subvolume", "delete", "-c", subvolume_folder.c_str(), NULL); } return rc==0; } else if(mode==mode_zfs) { zfs_elevate(); exec_wait(find_zfs_cmd(), false, "destroy", (subvolume_folder+"@ro").c_str(), NULL); int rc = exec_wait(find_zfs_cmd(), false, "destroy", subvolume_folder.c_str(), NULL); if(rc!=0) { std::cout << "Destroying subvol " << subvolume_folder << " failed. Promoting dependencies..." << std::endl; std::string rename_name = ExtractFileName(subvolume_folder); if(exec_wait(find_zfs_cmd(), true, "rename", (subvolume_folder+"@ro").c_str(), (subvolume_folder+"@"+rename_name).c_str(), NULL)!=0 && is_subvolume(mode, subvolume_folder+"@ro") ) { return false; } std::vector<std::string> dependencies; if(!promote_dependencies(subvolume_folder+"@"+rename_name, dependencies)) { return false; } rc = exec_wait(find_zfs_cmd(), true, "destroy", subvolume_folder.c_str(), NULL); if(rc==0) { for(size_t i=0;i<dependencies.size();++i) { if(is_subvolume(mode, dependencies[i]+"@"+rename_name)) { rc = exec_wait(find_zfs_cmd(), true, "destroy", (dependencies[i]+"@"+rename_name).c_str(), NULL); if(rc!=0) { break; } } } } } return rc==0; } return false; #endif }
int main(int argc, char *argv[]) { int fifo; int i, n; char *opt, *arg, *equal_arg, *homedir, *user; char *line, *eol, buf[4096]; pgm_name = argv[0]; bcm_host_init(); time(&pikrellcam.t_now); config_set_defaults(); for (i = 1; i < argc; i++) get_arg_pass1(argv[i]); if (!config_load(pikrellcam.config_file)) config_save(pikrellcam.config_file); if (!motion_regions_config_load(pikrellcam.motion_regions_config_file)) motion_regions_config_save(pikrellcam.motion_regions_config_file); if (!at_commands_config_load(pikrellcam.at_commands_config_file)) at_commands_config_save(pikrellcam.at_commands_config_file); for (i = 1; i < argc; i++) { if (get_arg_pass1(argv[i])) continue; opt = argv[i]; /* Just for initial install-pikrellcam.sh run to create config files. */ if (!strcmp(opt, "-quit")) exit(0); /* Accept: --opt arg -opt arg opt=arg --opt=arg -opt=arg */ for (i = 0; i < 2; ++i) if (*opt == '-') ++opt; if ((equal_arg = strchr(opt, '=')) != NULL) { *equal_arg++ = '\0'; arg = equal_arg; ++i; } else arg = argv[i + 1]; /* For camera parameters, do not set the camera, only replace | values in the parameter table. */ if ( !config_set_option(opt, arg, TRUE) && !mmalcam_config_parameter_set(opt, arg, FALSE) ) { log_printf("Bad arg: %s\n", opt); exit(1); } } homedir = getpwuid(geteuid())->pw_dir; user = strrchr(homedir, '/'); pikrellcam.effective_user = strdup(user ? user + 1 : "pi"); if (*pikrellcam.log_file != '/') { snprintf(buf, sizeof(buf), "%s/%s", pikrellcam.install_dir, pikrellcam.log_file); dup_string(&pikrellcam.log_file, buf); } if (*pikrellcam.media_dir != '/') { snprintf(buf, sizeof(buf), "%s/%s", pikrellcam.install_dir, pikrellcam.media_dir); dup_string(&pikrellcam.media_dir, buf); } strftime(buf, sizeof(buf), "%F %T", localtime(&pikrellcam.t_now)); log_printf("\n%s ==== PiKrellCam started ====\n", buf); snprintf(buf, sizeof(buf), "%s/%s", pikrellcam.install_dir, "www"); check_modes(buf, 0775); asprintf(&pikrellcam.command_fifo, "%s/www/FIFO", pikrellcam.install_dir); asprintf(&pikrellcam.script_dir, "%s/scripts", pikrellcam.install_dir); asprintf(&pikrellcam.mjpeg_filename, "%s/mjpeg.jpg", pikrellcam.mjpeg_dir); log_printf("using FIFO: %s\n", pikrellcam.command_fifo); log_printf("using mjpeg: %s\n", pikrellcam.mjpeg_filename); /* Subdirs must match www/config.php and the init script is supposed | to take care of that. */ asprintf(&pikrellcam.video_dir, "%s/%s", pikrellcam.media_dir, PIKRELLCAM_VIDEO_SUBDIR); asprintf(&pikrellcam.still_dir, "%s/%s", pikrellcam.media_dir, PIKRELLCAM_STILL_SUBDIR); asprintf(&pikrellcam.timelapse_dir, "%s/%s", pikrellcam.media_dir, PIKRELLCAM_TIMELAPSE_SUBDIR); if (!make_dir(pikrellcam.media_dir)) exit(1); snprintf(buf, sizeof(buf), "%s/scripts-dist/init $I $m $M $P $G", pikrellcam.install_dir); exec_wait(buf, NULL); /* User may have enabled a mount disk on media_dir */ exec_wait(pikrellcam.on_startup_cmd, NULL); check_modes(pikrellcam.media_dir, 0775); if ( !make_dir(pikrellcam.mjpeg_dir) || !make_dir(pikrellcam.video_dir) || !make_dir(pikrellcam.still_dir) || !make_dir(pikrellcam.timelapse_dir) || !make_fifo(pikrellcam.command_fifo) ) exit(1); if ((fifo = open(pikrellcam.command_fifo, O_RDONLY | O_NONBLOCK)) < 0) { log_printf("Failed to open FIFO: %s. %m\n", pikrellcam.command_fifo); exit(1); } fcntl(fifo, F_SETFL, 0); read(fifo, buf, sizeof(buf)); sun_times_init(); camera_start(); config_timelapse_load_status(); signal(SIGINT, signal_quit); signal(SIGTERM, signal_quit); signal(SIGCHLD, event_child_signal); while (1) { usleep(1000000 / EVENT_LOOP_FREQUENCY); event_process(); /* Process lines in the FIFO. Single lines via an echo "xxx" > FIFO | or from a web page may not have a terminating \n. | Local scripts may dump multiple \n terminated lines into the FIFO. */ if ((n = read(fifo, buf, sizeof(buf) - 2)) > 0) { if (buf[n - 1] != '\n') buf[n++] = '\n'; /* ensures all lines in buf end in \n */ buf[n] = '\0'; line = buf; eol = strchr(line, '\n'); while (eol > line) { *eol++ = '\0'; command_process(line); while (*eol == '\n') ++eol; line = eol; eol = strchr(line, '\n'); } } } return 0; }
void exec_cmd ( char * command, void (* func)( void * closure, int status, timing_info *, char * invoked_command, char * command_output ), void * closure, LIST * shell, char * action, char * target ) { int slot; int raw_cmd = 0 ; char * argv_static[ MAXARGC + 1 ]; /* +1 for NULL */ char * * argv = argv_static; char * p; char * command_orig = command; /* Check to see if we need to hack around the line-length limitation. Look * for a JAMSHELL setting of "%", indicating that the command should be * invoked directly. */ if ( shell && !strcmp( shell->string, "%" ) && !list_next( shell ) ) { raw_cmd = 1; shell = 0; } /* Find a slot in the running commands table for this one. */ for ( slot = 0; slot < MAXJOBS; ++slot ) if ( !cmdtab[ slot ].pi.hProcess ) break; if ( slot == MAXJOBS ) { printf( "no slots for child!\n" ); exit( EXITBAD ); } /* Compute the name of a temp batch file, for possible use. */ if ( !cmdtab[ slot ].tempfile_bat ) { char const * tempdir = path_tmpdir(); DWORD procID = GetCurrentProcessId(); /* SVA - allocate 64 bytes extra just to be safe. */ cmdtab[ slot ].tempfile_bat = BJAM_MALLOC_ATOMIC( strlen( tempdir ) + 64 ); sprintf( cmdtab[ slot ].tempfile_bat, "%s\\jam%d-%02d.bat", tempdir, procID, slot ); } /* Trim leading, -ending- white space */ while ( *( command + 1 ) && isspace( *command ) ) ++command; /* Write to .BAT file unless the line would be too long and it meets the * other spawnability criteria. */ if ( raw_cmd && ( can_spawn( command ) >= MAXLINE ) ) { if ( DEBUG_EXECCMD ) printf("Executing raw command directly\n"); } else { FILE * f = 0; int tries = 0; raw_cmd = 0; /* Write command to bat file. For some reason this open can fail * intermitently. But doing some retries works. Most likely this is due * to a previously existing file of the same name that happens to be * opened by an active virus scanner. Pointed out and fixed by Bronek * Kozicki. */ for ( ; !f && ( tries < 4 ); ++tries ) { f = fopen( cmdtab[ slot ].tempfile_bat, "w" ); if ( !f && ( tries < 4 ) ) Sleep( 250 ); } if ( !f ) { printf( "failed to write command file!\n" ); exit( EXITBAD ); } fputs( command, f ); fclose( f ); command = cmdtab[ slot ].tempfile_bat; if ( DEBUG_EXECCMD ) { if ( shell ) printf( "using user-specified shell: %s", shell->string ); else printf( "Executing through .bat file\n" ); } } /* Formulate argv; If shell was defined, be prepared for % and ! subs. * Otherwise, use stock cmd.exe. */ if ( shell ) { int i; char jobno[ 4 ]; int gotpercent = 0; sprintf( jobno, "%d", slot + 1 ); for ( i = 0; shell && ( i < MAXARGC ); ++i, shell = list_next( shell ) ) { switch ( shell->string[ 0 ] ) { case '%': argv[ i ] = command; ++gotpercent; break; case '!': argv[ i ] = jobno; break; default : argv[ i ] = shell->string; } if ( DEBUG_EXECCMD ) printf( "argv[%d] = '%s'\n", i, argv[ i ] ); } if ( !gotpercent ) argv[ i++ ] = command; argv[ i ] = 0; } else if ( raw_cmd ) { argv = string_to_args( command ); } else { argv[ 0 ] = "cmd.exe"; argv[ 1 ] = "/Q/C"; /* anything more is non-portable */ argv[ 2 ] = command; argv[ 3 ] = 0; } /* Catch interrupts whenever commands are running. */ if ( !cmdsrunning++ ) istat = signal( SIGINT, onintr ); /* Start the command. */ { SECURITY_ATTRIBUTES sa = { sizeof( SECURITY_ATTRIBUTES ), 0, 0 }; SECURITY_DESCRIPTOR sd; STARTUPINFO si = { sizeof( STARTUPINFO ), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; string cmd; /* Init the security data. */ InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION ); SetSecurityDescriptorDacl( &sd, TRUE, NULL, FALSE ); sa.lpSecurityDescriptor = &sd; sa.bInheritHandle = TRUE; /* Create the stdout, which is also the merged out + err, pipe. */ if ( !CreatePipe( &cmdtab[ slot ].pipe_out[ 0 ], &cmdtab[ slot ].pipe_out[ 1 ], &sa, 0 ) ) { perror( "CreatePipe" ); exit( EXITBAD ); } /* Create the stdout, which is also the merged out+err, pipe. */ if ( globs.pipe_action == 2 ) { if ( !CreatePipe( &cmdtab[ slot ].pipe_err[ 0 ], &cmdtab[ slot ].pipe_err[ 1 ], &sa, 0 ) ) { perror( "CreatePipe" ); exit( EXITBAD ); } } /* Set handle inheritance off for the pipe ends the parent reads from. */ SetHandleInformation( cmdtab[ slot ].pipe_out[ 0 ], HANDLE_FLAG_INHERIT, 0 ); if ( globs.pipe_action == 2 ) SetHandleInformation( cmdtab[ slot ].pipe_err[ 0 ], HANDLE_FLAG_INHERIT, 0 ); /* Hide the child window, if any. */ si.dwFlags |= STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; /* Set the child outputs to the pipes. */ si.dwFlags |= STARTF_USESTDHANDLES; si.hStdOutput = cmdtab[ slot ].pipe_out[ 1 ]; if ( globs.pipe_action == 2 ) { /* Pipe stderr to the action error output. */ si.hStdError = cmdtab[ slot ].pipe_err[ 1 ]; } else if ( globs.pipe_action == 1 ) { /* Pipe stderr to the console error output. */ si.hStdError = GetStdHandle( STD_ERROR_HANDLE ); } else { /* Pipe stderr to the action merged output. */ si.hStdError = cmdtab[ slot ].pipe_out[ 1 ]; } /* Let the child inherit stdin, as some commands assume it's available. */ si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); /* Save the operation for exec_wait() to find. */ cmdtab[ slot ].func = func; cmdtab[ slot ].closure = closure; if ( action && target ) { string_copy( &cmdtab[ slot ].action, action ); string_copy( &cmdtab[ slot ].target, target ); } else { string_free( &cmdtab[ slot ].action ); string_new ( &cmdtab[ slot ].action ); string_free( &cmdtab[ slot ].target ); string_new ( &cmdtab[ slot ].target ); } string_copy( &cmdtab[ slot ].command, command_orig ); /* Put together the command we run. */ { char * * argp = argv; string_new( &cmd ); string_copy( &cmd, *(argp++) ); while ( *argp ) { string_push_back( &cmd, ' ' ); string_append( &cmd, *(argp++) ); } } /* Create output buffers. */ string_new( &cmdtab[ slot ].buffer_out ); string_new( &cmdtab[ slot ].buffer_err ); /* Run the command by creating a sub-process for it. */ if ( ! CreateProcess( NULL , /* application name */ cmd.value , /* command line */ NULL , /* process attributes */ NULL , /* thread attributes */ TRUE , /* inherit handles */ CREATE_NEW_PROCESS_GROUP, /* create flags */ NULL , /* env vars, null inherits env */ NULL , /* current dir, null is our */ /* current dir */ &si , /* startup info */ &cmdtab[ slot ].pi /* child process info, if created */ ) ) { perror( "CreateProcess" ); exit( EXITBAD ); } /* Clean up temporary stuff. */ string_free( &cmd ); } /* Wait until we are under the limit of concurrent commands. Do not trust * globs.jobs alone. */ while ( ( cmdsrunning >= MAXJOBS ) || ( cmdsrunning >= globs.jobs ) ) if ( !exec_wait() ) break; if ( argv != argv_static ) free_argv( argv ); }
static void make1c( state const * const pState ) { TARGET * const t = pState->t; CMD * const cmd = (CMD *)t->cmds; if ( cmd && t->status == EXEC_CMD_OK ) { /* Pop state first in case something below (e.g. exec_cmd(), exec_wait() * or make1c_closure()) pushes a new state. Note that we must not access * the popped state data after this as the same stack node might have * been reused internally for some newly pushed state. */ pop_state( &state_stack ); /* Increment the jobs running counter. */ ++cmdsrunning; /* Execute the actual build command or fake it if no-op. */ if ( globs.noexec || cmd->noop ) { timing_info time_info = { 0 }; timestamp_current( &time_info.start ); timestamp_copy( &time_info.end, &time_info.start ); make1c_closure( t, EXEC_CMD_OK, &time_info, "", "", EXIT_OK ); } else { exec_cmd( cmd->buf, make1c_closure, t, cmd->shell ); /* Wait until under the concurrent command count limit. */ /* FIXME: This wait could be skipped here and moved to just before * trying to execute a command that would cross the command count * limit. Note though that this might affect the order in which * unrelated targets get built and would thus require that all * affected Boost Build tests be updated. */ assert( 0 < globs.jobs ); assert( globs.jobs <= MAXJOBS ); while ( cmdsrunning >= globs.jobs ) exec_wait(); } } else { ACTIONS * actions; /* Collect status from actions, and distribute it as well. */ for ( actions = t->actions; actions; actions = actions->next ) if ( actions->action->status > t->status ) t->status = actions->action->status; for ( actions = t->actions; actions; actions = actions->next ) if ( t->status > actions->action->status ) actions->action->status = t->status; /* Tally success/failure for those we tried to update. */ if ( t->progress == T_MAKE_RUNNING ) switch ( t->status ) { case EXEC_CMD_OK: ++counts->made; break; case EXEC_CMD_FAIL: ++counts->failed; break; } /* Tell parents their dependency has been built. */ { TARGETS * c; stack temp_stack = { NULL }; TARGET * additional_includes = NULL; t->progress = globs.noexec ? T_MAKE_NOEXEC_DONE : T_MAKE_DONE; /* Target has been updated so rescan it for dependencies. */ if ( t->fate >= T_FATE_MISSING && t->status == EXEC_CMD_OK && !( t->flags & T_FLAG_INTERNAL ) ) { TARGET * saved_includes; SETTINGS * s; t->rescanned = 1; /* Clean current includes. */ saved_includes = t->includes; t->includes = 0; s = copysettings( t->settings ); pushsettings( root_module(), s ); headers( t ); popsettings( root_module(), s ); freesettings( s ); if ( t->includes ) { /* Tricky. The parents have already been processed, but they * have not seen the internal node, because it was just * created. We need to: * - push MAKE1A states that would have been pushed by the * parents here * - make sure all unprocessed parents will pick up the * new includes * - make sure processing the additional MAKE1A states is * done before processing the MAKE1B state for our * current target (which would mean this target has * already been built), otherwise the parent would be * considered built before the additional MAKE1A state * processing even got a chance to start. */ make0( t->includes, t->parents->target, 0, 0, 0, t->includes ); /* Link the old includes on to make sure that it gets * cleaned up correctly. */ t->includes->includes = saved_includes; for ( c = t->dependants; c; c = c->next ) c->target->depends = targetentry( c->target->depends, t->includes ); /* Will be processed below. */ additional_includes = t->includes; } else { t->includes = saved_includes; } } if ( additional_includes ) for ( c = t->parents; c; c = c->next ) push_state( &temp_stack, additional_includes, c->target, T_STATE_MAKE1A ); if ( t->scc_root ) { TARGET * const scc_root = target_scc( t ); assert( scc_root->progress < T_MAKE_DONE ); for ( c = t->parents; c; c = c->next ) { if ( target_scc( c->target ) == scc_root ) push_state( &temp_stack, c->target, NULL, T_STATE_MAKE1B ); else scc_root->parents = targetentry( scc_root->parents, c->target ); } } else { for ( c = t->parents; c; c = c->next ) push_state( &temp_stack, c->target, NULL, T_STATE_MAKE1B ); } #ifdef OPT_SEMAPHORE /* If there is a semaphore, it is now free. */ if ( t->semaphore ) { assert( t->semaphore->asynccnt == 1 ); --t->semaphore->asynccnt; if ( DEBUG_EXECCMD ) printf( "SEM: %s is now free\n", object_str( t->semaphore->name ) ); /* If anything is waiting, notify the next target. There is no * point in notifying all waiting targets, since they will be * notified again. */ if ( t->semaphore->parents ) { TARGETS * first = t->semaphore->parents; t->semaphore->parents = first->next; if ( first->next ) first->next->tail = first->tail; if ( DEBUG_EXECCMD ) printf( "SEM: placing %s on stack\n", object_str( first->target->name ) ); push_state( &temp_stack, first->target, NULL, T_STATE_MAKE1B ); BJAM_FREE( first ); } } #endif /* Must pop state before pushing any more. */ pop_state( &state_stack ); /* Using stacks reverses the order of execution. Reverse it back. */ push_stack_on_stack( &state_stack, &temp_stack ); } } }
void exec_cmd ( char * string, void (*func)( void *closure, int status, timing_info*, char *, char * ), void * closure, LIST * shell, char * action, char * target ) { static int initialized = 0; int out[2]; int err[2]; int slot; int len; char * argv[ MAXARGC + 1 ]; /* +1 for NULL */ /* Find a slot in the running commands table for this one. */ for ( slot = 0; slot < MAXJOBS; ++slot ) if ( !cmdtab[ slot ].pid ) break; if ( slot == MAXJOBS ) { printf( "no slots for child!\n" ); exit( EXITBAD ); } /* Forumulate argv. If shell was defined, be prepared for % and ! subs. * Otherwise, use stock /bin/sh on unix or cmd.exe on NT. */ if ( shell ) { int i; char jobno[4]; int gotpercent = 0; sprintf( jobno, "%d", slot + 1 ); for ( i = 0; shell && i < MAXARGC; ++i, shell = list_next( shell ) ) { switch ( shell->string[0] ) { case '%': argv[ i ] = string; ++gotpercent; break; case '!': argv[ i ] = jobno; break; default : argv[ i ] = shell->string; } if ( DEBUG_EXECCMD ) printf( "argv[%d] = '%s'\n", i, argv[ i ] ); } if ( !gotpercent ) argv[ i++ ] = string; argv[ i ] = 0; } else { argv[ 0 ] = "/bin/sh"; argv[ 1 ] = "-c"; argv[ 2 ] = string; argv[ 3 ] = 0; } /* Increment jobs running. */ ++cmdsrunning; /* Save off actual command string. */ cmdtab[ slot ].command = BJAM_MALLOC_ATOMIC( strlen( string ) + 1 ); strcpy( cmdtab[ slot ].command, string ); /* Initialize only once. */ if ( !initialized ) { times( &old_time ); initialized = 1; } /* Create pipes from child to parent. */ { if ( pipe( out ) < 0 ) exit( EXITBAD ); fcntl( out[0], F_SETFL, O_NONBLOCK ); fcntl( out[1], F_SETFL, O_NONBLOCK ); if ( pipe( err ) < 0 ) exit( EXITBAD ); fcntl( err[0], F_SETFL, O_NONBLOCK ); fcntl( err[1], F_SETFL, O_NONBLOCK ); } /* Start the command */ cmdtab[ slot ].start_dt = time(0); if ( 0 < globs.timeout ) { /* * Handle hung processes by manually tracking elapsed time and signal * process when time limit expires. */ struct tms buf; cmdtab[ slot ].start_time = times( &buf ); /* Make a global, only do this once. */ if ( tps == 0 ) tps = sysconf( _SC_CLK_TCK ); } if ( ( cmdtab[ slot ].pid = vfork() ) == 0 ) { int pid = getpid(); close( out[0] ); close( err[0] ); dup2( out[1], STDOUT_FILENO ); if ( globs.pipe_action == 0 ) { dup2( out[1], STDERR_FILENO ); close( err[1] ); } else dup2( err[1], STDERR_FILENO ); /* Make this process a process group leader so that when we kill it, all * child processes of this process are terminated as well. We use * killpg(pid, SIGKILL) to kill the process group leader and all its * children. */ if ( 0 < globs.timeout ) { struct rlimit r_limit; r_limit.rlim_cur = globs.timeout; r_limit.rlim_max = globs.timeout; setrlimit( RLIMIT_CPU, &r_limit ); } setpgid( pid,pid ); execvp( argv[0], argv ); perror( "execvp" ); _exit( 127 ); } else if ( cmdtab[ slot ].pid == -1 ) { perror( "vfork" ); exit( EXITBAD ); } setpgid( cmdtab[ slot ].pid, cmdtab[ slot ].pid ); /* close write end of pipes */ close( out[1] ); close( err[1] ); /* child writes stdout to out[1], parent reads from out[0] */ cmdtab[ slot ].fd[ OUT ] = out[0]; cmdtab[ slot ].stream[ OUT ] = fdopen( cmdtab[ slot ].fd[ OUT ], "rb" ); if ( cmdtab[ slot ].stream[ OUT ] == NULL ) { perror( "fdopen" ); exit( EXITBAD ); } /* child writes stderr to err[1], parent reads from err[0] */ if (globs.pipe_action == 0) { close(err[0]); } else { cmdtab[ slot ].fd[ ERR ] = err[0]; cmdtab[ slot ].stream[ ERR ] = fdopen( cmdtab[ slot ].fd[ ERR ], "rb" ); if ( cmdtab[ slot ].stream[ ERR ] == NULL ) { perror( "fdopen" ); exit( EXITBAD ); } } /* Ensure enough room for rule and target name. */ if ( action && target ) { len = strlen( action ) + 1; if ( cmdtab[ slot ].action_length < len ) { BJAM_FREE( cmdtab[ slot ].action ); cmdtab[ slot ].action = BJAM_MALLOC_ATOMIC( len ); cmdtab[ slot ].action_length = len; } strcpy( cmdtab[ slot ].action, action ); len = strlen( target ) + 1; if ( cmdtab[ slot ].target_length < len ) { BJAM_FREE( cmdtab[ slot ].target ); cmdtab[ slot ].target = BJAM_MALLOC_ATOMIC( len ); cmdtab[ slot ].target_length = len; } strcpy( cmdtab[ slot ].target, target ); } else { BJAM_FREE( cmdtab[ slot ].action ); BJAM_FREE( cmdtab[ slot ].target ); cmdtab[ slot ].action = 0; cmdtab[ slot ].target = 0; cmdtab[ slot ].action_length = 0; cmdtab[ slot ].target_length = 0; } /* Save the operation for exec_wait() to find. */ cmdtab[ slot ].func = func; cmdtab[ slot ].closure = closure; /* Wait until we are under the limit of concurrent commands. Do not trust * globs.jobs alone. */ while ( ( cmdsrunning >= MAXJOBS ) || ( cmdsrunning >= globs.jobs ) ) if ( !exec_wait() ) break; }
bool torture_gpo_system_access_policies(struct torture_context *tctx) { TALLOC_CTX *ctx = talloc_new(tctx); int ret, vers = 0, i; const char *sysvol_path = NULL, *gpo_dir = NULL; const char *gpo_file = NULL, *gpt_file = NULL; struct ldb_context *samdb = NULL; struct ldb_result *result; const char *attrs[] = { "minPwdAge", "maxPwdAge", "minPwdLength", "pwdProperties", NULL }; FILE *fp = NULL; const char **gpo_update_cmd; char **gpo_unapply_cmd; int minpwdcases[] = { 0, 1, 998 }; int maxpwdcases[] = { 0, 1, 999 }; int pwdlencases[] = { 0, 1, 14 }; int pwdpropcases[] = { 0, 1, 1 }; struct ldb_message *old_message = NULL; const char **itr; int gpo_update_len = 0; sysvol_path = lpcfg_path(lpcfg_service(tctx->lp_ctx, "sysvol"), lpcfg_default_service(tctx->lp_ctx), tctx); torture_assert(tctx, sysvol_path, "Failed to fetch the sysvol path"); /* Ensure the sysvol path exists */ gpo_dir = talloc_asprintf(ctx, "%s/%s", sysvol_path, GPODIR); mkdir_p(gpo_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); gpo_file = talloc_asprintf(ctx, "%s/%s", gpo_dir, GPOFILE); /* Get the gpo update command */ gpo_update_cmd = lpcfg_gpo_update_command(tctx->lp_ctx); torture_assert(tctx, gpo_update_cmd && gpo_update_cmd[0], "Failed to fetch the gpo update command"); /* Open and read the samba db and store the initial password settings */ samdb = samdb_connect(ctx, tctx->ev, tctx->lp_ctx, system_session(tctx->lp_ctx), NULL, 0); torture_assert(tctx, samdb, "Failed to connect to the samdb"); ret = ldb_search(samdb, ctx, &result, ldb_get_default_basedn(samdb), LDB_SCOPE_BASE, attrs, NULL); torture_assert(tctx, ret == LDB_SUCCESS && result->count == 1, "Searching the samdb failed"); old_message = result->msgs[0]; for (i = 0; i < 3; i++) { /* Write out the sysvol */ if ( (fp = fopen(gpo_file, "w")) ) { fputs(talloc_asprintf(ctx, GPTTMPL, minpwdcases[i], maxpwdcases[i], pwdlencases[i], pwdpropcases[i]), fp); fclose(fp); } /* Update the version in the GPT.INI */ gpt_file = talloc_asprintf(ctx, "%s/%s", sysvol_path, GPTINI); if ( (fp = fopen(gpt_file, "r")) ) { char line[256]; while (fgets(line, 256, fp)) { if (strncasecmp(line, "Version=", 8) == 0) { vers = atoi(line+8); break; } } fclose(fp); } if ( (fp = fopen(gpt_file, "w")) ) { char *data = talloc_asprintf(ctx, "[General]\nVersion=%d\n", ++vers); fputs(data, fp); fclose(fp); } /* Run the gpo update command */ ret = exec_wait(discard_const_p(char *, gpo_update_cmd)); torture_assert(tctx, ret == 0, "Failed to execute the gpo update command"); ret = ldb_search(samdb, ctx, &result, ldb_get_default_basedn(samdb), LDB_SCOPE_BASE, attrs, NULL); torture_assert(tctx, ret == LDB_SUCCESS && result->count == 1, "Searching the samdb failed"); /* minPwdAge */ torture_assert_int_equal(tctx, unix2nttime( ldb_msg_find_attr_as_string( result->msgs[0], attrs[0], "")), minpwdcases[i], "The minPwdAge was not applied"); /* maxPwdAge */ torture_assert_int_equal(tctx, unix2nttime( ldb_msg_find_attr_as_string( result->msgs[0], attrs[1], "")), maxpwdcases[i], "The maxPwdAge was not applied"); /* minPwdLength */ torture_assert_int_equal(tctx, ldb_msg_find_attr_as_int( result->msgs[0], attrs[2], -1), pwdlencases[i], "The minPwdLength was not applied"); /* pwdProperties */ torture_assert_int_equal(tctx, ldb_msg_find_attr_as_int( result->msgs[0], attrs[3], -1), pwdpropcases[i], "The pwdProperties were not applied"); } /* Unapply the settings and verify they are removed */ for (itr = gpo_update_cmd; *itr != NULL; itr++) { gpo_update_len++; } gpo_unapply_cmd = talloc_array(ctx, char*, gpo_update_len+2); for (i = 0; i < gpo_update_len; i++) { gpo_unapply_cmd[i] = talloc_strdup(gpo_unapply_cmd, gpo_update_cmd[i]); } gpo_unapply_cmd[i] = talloc_asprintf(gpo_unapply_cmd, "--unapply"); gpo_unapply_cmd[i+1] = NULL; ret = exec_wait(gpo_unapply_cmd); torture_assert(tctx, ret == 0, "Failed to execute the gpo unapply command"); ret = ldb_search(samdb, ctx, &result, ldb_get_default_basedn(samdb), LDB_SCOPE_BASE, attrs, NULL); torture_assert(tctx, ret == LDB_SUCCESS && result->count == 1, "Searching the samdb failed"); /* minPwdAge */ torture_assert_int_equal(tctx, unix2nttime(ldb_msg_find_attr_as_string( result->msgs[0], attrs[0], "")), unix2nttime(ldb_msg_find_attr_as_string(old_message, attrs[0], "") ), "The minPwdAge was not unapplied"); /* maxPwdAge */ torture_assert_int_equal(tctx, unix2nttime(ldb_msg_find_attr_as_string( result->msgs[0], attrs[1], "")), unix2nttime(ldb_msg_find_attr_as_string(old_message, attrs[1], "") ), "The maxPwdAge was not unapplied"); /* minPwdLength */ torture_assert_int_equal(tctx, ldb_msg_find_attr_as_int( result->msgs[0], attrs[2], -1), ldb_msg_find_attr_as_int( old_message, attrs[2], -2), "The minPwdLength was not unapplied"); /* pwdProperties */ torture_assert_int_equal(tctx, ldb_msg_find_attr_as_int( result->msgs[0], attrs[3], -1), ldb_msg_find_attr_as_int( old_message, attrs[3], -2), "The pwdProperties were not unapplied"); talloc_free(ctx); return true; }