This is ExecRunCommand.m in view mode; [Download] [Up]
// ------------------------------------------------------------------------------------- // ExecRunCommand.m // (Indent:4, Tabs:4) // ------------------------------------------------------------------------------------- // Copyright 1996 Persistent Technologies, Inc. - all rights reserved // ------------------------------------------------------------------------------------- // This source code comes with no warranty of any kind, and the user assumes all // responsibility for its use. // ------------------------------------------------------------------------------------- #import <appkit/appkit.h> #import <libc.h> #import <mach/cthreads.h> #import <stdlib.h> #import <stdarg.h> #import <string.h> #import <pwd.h> #import <sys/types.h> #import <sys/wait.h> #import "ExecRunCommand.h" // ------------------------------------------------------------------------------------- // pending pid list - keep track of child process creation and termination /* pid list */ #define MAX_PIDLIST_SIZE 64 /* maximum concurrent child processes */ typedef struct { int pid; int didExit; union wait status; } pidStatus_t; static pidStatus_t pidList[MAX_PIDLIST_SIZE] = { 0 }; static int pidCount = 0; /* return pidList index of specified pid number */ static int _pid_index(int pid) { if (pid > 0) { int n; for (n = 0; n < pidCount; n++) { if (pid == pidList[n].pid) return n; } } return -1; // not found } /* add pid to list (does not check to see if it already exists!) */ static int _pid_add(int thePid) { int ndx; for (ndx = 0; (ndx < pidCount) && pidList[ndx].pid; ndx++); pidList[ndx].pid = thePid; pidList[ndx].didExit = NO; if (ndx == pidCount) pidCount++; // upper bounds (MAX_PIDLIST_SIZE) not checked return ndx; } /* wait for pid to exit (keep track of all pid exit reports) */ static BOOL _pid_wait(int thePid, union wait *rtnStat) { int pidNdx = _pid_index(thePid); if (pidNdx < 0) pidNdx = _pid_add(thePid); //(ERROR) NOT FOUND - add anyway /* wait for pid to exit */ if (!(pidList[pidNdx].didExit)) { for (;;) { union wait status; int pid = wait(&status); if (pid == -1) { pidNdx = 0; break; }; pidNdx = _pid_index(pid); if (pidNdx < 0) pidNdx = _pid_add(pid); //(ERROR) NOT FOUND - add anyway pidList[pidNdx].didExit = YES; pidList[pidNdx].status = status; if (thePid == pid) break; } } /* return reported status */ if (pidNdx >= 0) { *rtnStat = pidList[pidNdx].status; pidList[pidNdx].pid = 0; return YES; } return NO; } // ------------------------------------------------------------------------------------- // ExecRunCommand private methods @interface ExecRunCommand(Private) + (struct passwd*)_getpwnam:(const char*)user; + (BOOL)_isRoot; + (int)_setenv:(char**)ep :(char*)eVal :(char*)fmt, ...; - (BOOL)_runCommand:(const char*)command user:(const char*)user; - (void)_sendSignal:(int)sigval; - (void)_stopCommand; - (int)_popen:(const char*)cmd user:(const char*)user; - (int)_pclose; - (void)_gotData; static void gotData(int fd, void *self); - (void)_commandOutput:(const char*)buf len:(int)len; @end // -------------------------------------------------------------------------------- @implementation ExecRunCommand // -------------------------------------------------------------------------------- /* initialize */ - init { [super init]; cmdChild = 0; inputDescriptor = 0; delegate = nil; tag = 0; return self; } /* free */ - _free:sender { return [self free]; } - free { if (cmdChild > 0) { fprintf(stderr,"cannot free yet, killing command ...\n"); [self killCommand]; [self perform:@selector(_free:) with:self afterDelay:500 cancelPrevious:YES]; return (id)nil; } [super free]; return (id)nil; } // -------------------------------------------------------------------------------- /* run command */ + runCommand:(const char*)command user:(const char*)user output:(id)theDelegate { BOOL didStart; ExecRunCommand *mySelf = [[self alloc] init]; [mySelf setDelegate:theDelegate]; didStart = [mySelf _runCommand:command user:user]; if (!didStart) { [mySelf free]; mySelf = (id)nil; } return mySelf; } /* run command */ + runCommand:(const char*)command output:(id)theDelegate { return [ExecRunCommand runCommand:command user:(char*)nil output:theDelegate]; } /* similar to "system(...)" */ + (int)system:(const char*)command user:(const char*)user output:(id)theDelegate { int err, infd; ExecRunCommand *mySelf = [[self alloc] init]; [mySelf setDelegate:theDelegate]; infd = [mySelf _popen:command user:user]; if (infd >= 0) { int cnt; char buffer[1025]; do { cnt = read(infd, buffer, sizeof(buffer)); if (cnt != -1) [mySelf _commandOutput:buffer len:cnt]; } while (cnt > 0); err = [mySelf _pclose]; close(infd); } else err = RUNCMD_EXEC; // could not execute mySelf->cmdChild = 0; [mySelf free]; return err; } /* see "system:user:output:" */ + (int)system:(const char*)command user:(const char*)user { return [self system:command user:user output:nil]; } /* see "system:user:output:" */ + (int)system:(const char*)command { return [self system:command user:(char*)nil output:nil]; } /* set tag */ - setTag:(int)theTag { tag = theTag; return self; } /* return tag */ - (int)tag { return tag; } /* return true if command is still active */ - (BOOL)isActive { return (cmdChild > 0)? YES : NO; } /* set delegate */ - setDelegate:(id)theDelegate { delegate = theDelegate; return self; } /* return delegate */ - (id)delegate { return delegate; } /* interrupt command */ - interruptCommand { [self _sendSignal:SIGINT]; return self; } /* terminate command */ - terminateCommand { [self _sendSignal:SIGTERM]; return self; } /* kill command */ - killCommand { [self _sendSignal:SIGKILL]; return self; } /* return true if running with effective user root */ + (BOOL)isRunningAsRoot { return [self _isRoot]; } // -------------------------------------------------------------------------------- /* wrapper for getpwnam() */ + (struct passwd*)_getpwnam:(const char*)user { extern void _lu_setport(port_t); extern port_t _lookupd_port(int); _lu_setport(_lookupd_port(0)); // may not be necessary post v3.0 return user? getpwnam(user) : getpwuid(getuid()); } /* remote: return 'root' flag */ + (BOOL)_isRoot { return ([ExecRunCommand _getpwnam:"root"]->pw_uid == geteuid())? YES : NO; } /* set environment variable */ + (int)_setenv:(char**)ep :(char*)eVal :(char*)fmt, ... { va_list args; register char *cp, *dp; va_start(args, fmt); vsprintf(eVal, fmt, args); va_end(args); for (;dp = *ep; ep++) { for (cp = eVal; (*cp == *dp) && (*cp != '=') && *cp; cp++, dp++) continue; if (((*cp == '=') || !*cp) && ((*dp == '=') || !*dp)) { *ep = eVal; return 0; } } return -1; } // -------------------------------------------------------------------------------- /* execute a command */ - (BOOL)_runCommand:(const char*)command user:(const char*)user { inputDescriptor = [self _popen:command user:user]; if (inputDescriptor >= 0) { DPSAddFD(inputDescriptor, gotData, self, NX_BASETHRESHOLD); return YES; } return NO; } /* send signal */ - (void)_sendSignal:(int)sigval { if (cmdChild > 0) { killpg(cmdChild, sigval); kill(cmdChild, sigval); } } /* stop command */ - (void)_stopCommand { int error; DPSRemoveFD(inputDescriptor); error = [self _pclose]; close(inputDescriptor); [self commandDidCompleteWithError:error]; } /* open pipe to shell and execute command */ - (int)_popen:(const char*)cmd user:(const char*)user { int inputP[2], hisOutput, myInput; const char **locEnv = environ; /* only "root" can specify a user */ if (user && ![[self class] _isRoot]) return -1; /* "root" user commands must be preceeded with "{root}..." */ if (user && !strcmp(user,"root")) { const char *match = "{root}"; int matchLen = strlen(match); if (strncmp(cmd,match,matchLen)) return -1; cmd += matchLen; } /* open pipe */ pipe(inputP); myInput = inputP[0]; hisOutput = inputP[1]; /* fork and execute shell */ if ((cmdChild = vfork()) == 0) { int i; char **env; setpgrp(0, getpid()); /* set up pipe handles */ close(myInput); if (hisOutput != 1) dup2(hisOutput, 1); if (hisOutput != 2) dup2(hisOutput, 2); if ((hisOutput != 1) && (hisOutput != 2)) close(hisOutput); /* make local copy of environment table */ for (i = 0; locEnv[i]; i++); env = (char**)alloca(sizeof(char*) * (i + 2 + 1)); // allocate on stack memset(env, 0, sizeof(char*) * (i + 2 + 1)); memcpy(env, locEnv, sizeof(char*) * i); /* switch to user */ if ([ExecRunCommand _isRoot]) { struct passwd *pw = [ExecRunCommand _getpwnam:user]; if ((setgid(pw->pw_gid) < 0) || (initgroups(pw->pw_name,pw->pw_gid)) || (setuid(pw->pw_uid) < 0)) { _exit(RUNCMD_USER); // cannot switch to user } [ExecRunCommand _setenv:env:(char*)alloca(strlen(pw->pw_dir)+7) :"HOME=%s",pw->pw_dir]; [ExecRunCommand _setenv:env:(char*)alloca(strlen(pw->pw_name)+7) :"USER=%s",pw->pw_name]; } /* execute command */ // execle("/bin/csh","csh","-f","-c",cmd,(char*)nil,env); execle("/bin/sh","sh","-c",cmd,(char*)nil,env); _exit(RUNCMD_EXEC); } /* set io */ if (cmdChild == -1) { close(myInput); myInput = -1; } else _pid_add(cmdChild); close(hisOutput); return myInput; } /* close shell command pipe */ - (int)_pclose { union wait status; int omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); BOOL flag = _pid_wait(cmdChild,&status); (void)sigsetmask(omask); if (!flag) return 0; // no children if (status.w_status & 0xFF) return RUNCMD_STOPPED; // process was terminated return (status.w_status >> 8) & 0xFF; } /* filter for data piped from command shell */ - (void)_gotData { char data[1024]; int n, cnt = 0; /* read available text */ do { if ((n = read(inputDescriptor,data,sizeof(data))) >= 0) { cnt += n; if (n) [self _commandOutput:data len:n]; } } while (n == sizeof(data)); /* stop command when done */ if (!cnt) [self _stopCommand]; } /* fd routine for receiving data */ static void gotData(int fd, void *self) { [(ExecRunCommand*)self _gotData]; } // -------------------------------------------------------------------------------- // support for remote shell server output /* copy text to scrollView (MAIN THREAD ONLY!) */ - (void)_commandOutput:(const char*)buf len:(int)len { if (len && buf) { if (delegate && [delegate respondsTo:@selector(commandOutput:buffer:len:)]) { [delegate commandOutput:self buffer:buf len:len]; } } } /* copy text to scrollView (MAIN THREAD ONLY!) */ - (oneway void)commandOutput:(const char*)buf len:(int)len { if (len && buf) { [self _commandOutput:buf len:len]; free((char*)buf); } } /* indicate that the shell has completed */ - (oneway void)commandDidCompleteWithError:(int)errorCode; { if (delegate && [delegate respondsTo:@selector(commandDidComplete:withError:)]) { [delegate commandDidComplete:self withError:errorCode]; } cmdChild = 0; [self free]; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.