This is ExecServer.m in view mode; [Download] [Up]
// ------------------------------------------------------------------------------------- // ExecServer // ------------------------------------------------------------------------------------- // Permission is granted to freely redistribute this source code, and to use fragments // of this code in your own applications if you find them to be useful. This class, // along with the source code, come with no warranty of any kind, and the user assumes // all responsibility for its use. // ------------------------------------------------------------------------------------- #import <appkit/appkit.h> #import <libc.h> #import <mach/mach.h> #import <mach/cthreads.h> #import <stdlib.h> #import <string.h> #import <c.h> #import <pwd.h> #import <sys/param.h> #import <sys/types.h> #import <sys/stat.h> #import <sys/time.h> #import <sys/dir.h> #import <mach/mach_traps.h> #import <remote/NXConnection.h> #import <remote/NXProxy.h> #import <machkit/NXPort.h> #import "ExecServer.h" // ------------------------------------------------------------------------------------- // These variables are set in the main process, then reset in the child server process uid_t exeUserUid; // login user uid uid_t exeRootUid; // root user uid // ------------------------------------------------------------------------------------- // Keep track of child process level. Used for debugging purposes only. int exeChildLevel = 0; #define forkCHILD ({extern int exeChildLevel;int c=fork();if(!c)exeChildLevel++;c;}) // ------------------------------------------------------------------------------------- // local implementation of NXCopyStringBuffer() #define STRCOPY(X) strcpy((char*)malloc(strlen(X) + 1), (X)); // ************************************************************************************* // ************************************************************************************* // _ExecServer_d / RunCommand declarations // ************************************************************************************* // ************************************************************************************* // ------------------------------------------------------------------------------------- // ExecServer shared status structure typedef struct _shareStat_s { int errCode; char errMsg[512]; char execScript[MAXPATHLEN + 1]; } shareStat_t; // ------------------------------------------------------------------------------------- // Root_process server protocols @protocol RunService - (void)_pingServer; - (BOOL)_isRunningAsRoot; - (execHandle_t)_runCommand:(const char*)command user:(const char*)userName:(const char*)password client:(id <RemoteClient>)theClient kill:(BOOL)killOnError; - (int)_uperform:(SEL)method withArg:(const char*)arg user:(const char*)userName:(const char*)password; - (void)_terminateCommand:(execHandle_t)runId; - (void)_killCommand:(execHandle_t)runId; - (BOOL)_childIsActive:(execHandle_t)runId; - (void)_shutDownServer; @end @protocol RootInternal // sent by RunCommander to server - (void)_commandOutput:(const char*)buf len:(int)len execId:(u_int)fakeRun; - (void)_commandDidComplete:(u_int)fakeRun; @end // ------------------------------------------------------------------------------------- // RunCommand declaration @interface RunCommand : Object <NXSenderIsInvalid> { @public int inputDescriptor; // input to csh pipe id client; // object to send csh output BOOL killOnError; // kill child process on 'senderIsInvalid:' id server; // _ExecServerd int cmdChild; // child pid int exitErr; // returned exit status shareStat_t *shareMem; // common memory for error information char *cmdName; // optional name } - (int)_pipeExec:(const char*)cmd user:(const char*)userName:(const char*)password; - (void)_execCommand:(const char*)cmd user:(const char*)userName:(const char*)password; - (void)_terminateCommand; - (void)_killCommand; @end // ------------------------------------------------------------------------------------- // _ExecServerd definition @interface _ExecServerd : Object <NXSenderIsInvalid, RunService, RootInternal> { @public id childList; char *serverName; id server; int clientCount; } + setExecServerOwner:theOwner; @end // ************************************************************************************* // ************************************************************************************* // _ExecServerd/RunCommand implementation // ************************************************************************************* // ************************************************************************************* // ------------------------------------------------------------------------------------- // These variables are only set within the child server process static ExecServer *exeServerOwner = (id)nil; // ExecServer instance owner // ------------------------------------------------------------------------------------- // These variables are statically set, and are never changed static BOOL _debugMode = NO; // debug mode // ------------------------------------------------------------------------------------- // static utility functions /* wait for specified child to exit */ static int _waitForExit(int child) { int pid, omask; union wait status; omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); while (((pid = wait(&status)) != child) && (pid != -1)); (void)sigsetmask(omask); return (status.w_status & 0xFF)? -1 : (status.w_status >> 8) & 0xFF; } /* check password */ static BOOL _checkPass(const char *pass, const char *pw_passwd) { char *cp; if (!pw_passwd || !*pw_passwd) return NO; cp = crypt((char*)pass, (char*)pw_passwd); return strcmp(cp, pw_passwd)? NO : YES; } /* set environment variable */ static 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; } // ------------------------------------------------------------------------------------- // global user functions /* wrapper for getpwnam() */ struct passwd *exeGetpwnam(const char *user) { extern void _lu_setport(port_t); extern port_t _lookupd_port(int); _lu_setport(_lookupd_port(0)); return user? getpwnam(user) : getpwuid(getuid()); } /* wrapper for getpwuid() */ struct passwd *exeGetpwuid(uid_t uid) { extern void _lu_setport(port_t); extern port_t _lookupd_port(int); _lu_setport(_lookupd_port(0)); return getpwuid(uid); } /* switch to specified user */ int exeSwitchToUser(const char *user, const char *pass) { struct passwd *pw; uid_t user_uid; gid_t user_gid; char user_name[128], user_passwd[128]; extern uid_t exeUserUid; // login user uid extern uid_t exeRootUid; // root user uid extern ExecServer *exeServerOwner; // ExecServer instance owner /* get user passwd record (cache values) */ if (!(pw = exeGetpwnam(user))) return RSRV_BADUSER; user_uid = pw->pw_uid; user_gid = pw->pw_gid; strcpy(user_name, pw->pw_name); strcpy(user_passwd, pw->pw_passwd); /* verify password (only in server children) */ if (exeServerOwner) { if (pass) { if (!_checkPass(pass, user_passwd)) { struct passwd *rpw = exeGetpwuid(exeRootUid); if (!rpw || !_checkPass(pass, rpw->pw_passwd)) return RSRV_BADPASSWD; } } else { if (user_uid != exeUserUid) return RSRV_BADPASSWD; } } /* switch to user */ if (setgid(user_gid) < 0) return RSRV_BADGID; if ((exeRootUid==geteuid()) && initgroups(user_name,user_gid)) return RSRV_BADINIT; if (setuid(user_uid) < 0) return RSRV_BADUID; /* return successful */ return 0; } // ------------------------------------------------------------------------------------- // shared memory functions /* allocate shared memory (used by Root_process) */ static void *_share_alloc(int size) { kern_return_t ret; vm_address_t _share_addr; ret = vm_allocate(task_self(), &_share_addr, size, 1); if (ret != KERN_SUCCESS) { NXLogError("[_share_alloc] vm_allocate() error %d", ret); _exit(1); } ret = vm_inherit(task_self(), _share_addr, size, VM_INHERIT_SHARE); if (ret != KERN_SUCCESS) { NXLogError("[_share_alloc] vm_inherit() error %d", ret); _exit(1); } memset((char*)_share_addr, 0, size); return (void*)_share_addr; } /* free shared memory */ static kern_return_t _share_free(void *_share_mem, int size) { kern_return_t ret; ret = vm_deallocate(task_self(), (vm_address_t)_share_mem, size); return ret; } // ------------------------------------------------------------------------------------- // RunCommand implementation @implementation RunCommand /* initialization */ - init { [super init]; inputDescriptor = -1; client = (id)nil; killOnError = YES; server = (id)nil; cmdChild = -1; exitErr = 0; shareMem = (shareStat_t*)_share_alloc(sizeof(shareStat_t)); return self; } /* free */ - free { kern_return_t ret; ret = _share_free((void*)shareMem, sizeof(shareStat_t)); if (ret != KERN_SUCCESS) { NXLogError("[RunCommand] vm_deallocate() error %d", ret); _exit(1); } return [super free]; } // ------------------------------------------------------------------------------------- /* open pipe to shell and execute command */ - (int)_pipeExec:(const char*)command user:(const char*)userName:(const char*)password { int inputP[2], hisOutput, myInput; const char **locEnv = environ; extern ExecServer *exeServerOwner; // ExecServer instance owner /* open pipe */ pipe(inputP); myInput = inputP[0]; hisOutput = inputP[1]; /* fork and execute User_process shell */ if ((cmdChild = forkCHILD) == 0) { char **env, *cmd = (char*)command; int envi; setpgrp(getpid(), getpid()); /* reset pipe */ close(myInput); if (hisOutput != 1) dup2(hisOutput, 1); if (hisOutput != 2) dup2(hisOutput, 2); if ((hisOutput != 1) && (hisOutput != 2)) close(hisOutput); /* set user */ if (shareMem->errCode = exeSwitchToUser(userName, password)) { sprintf(shareMem->errMsg, "user=%s, pass=%s", (userName?userName:""), (password?password:"")); _exit(0); } /* make local copy of environment table */ for (envi = 0; locEnv[envi]; envi++); // count entries env = (char**)alloca(sizeof(char*) * (envi + 3 + 1)); // on stack (extra if needed) memset(env, 0, sizeof(char*) * (envi + 3 + 1)); memcpy(env, locEnv, sizeof(char*) * envi); /* set path to app */ if (*(exeServerOwner->mainAppPath)) { env[envi] = (char*)alloca(strlen(exeServerOwner->mainAppPath) + 8 + 2); sprintf(env[envi], "CMDPATH=%s", exeServerOwner->mainAppPath); envi++; } /* set server name */ if (*(exeServerOwner->mainAppServerName)) { env[envi] = (char*)alloca(strlen(exeServerOwner->mainAppServerName) + 11 + 2); sprintf(env[envi], "SERVERNAME=%s", exeServerOwner->mainAppServerName); envi++; if (*(exeServerOwner->mainAppHost)) { env[envi] = (char*)alloca(strlen(exeServerOwner->mainAppHost) + 11 + 2); sprintf(env[envi], "SERVERHOST=%s", exeServerOwner->mainAppHost); envi++; } } /* update environment if user change */ if (userName && *userName) { struct passwd *upw = exeGetpwnam(userName); _setenv(env,(char*)alloca(strlen(upw->pw_dir)+7),"HOME=%s",upw->pw_dir); _setenv(env,(char*)alloca(strlen(userName)+7),"USER=%s",userName); } /* check for script file wrapper */ memset(shareMem->execScript, 0, sizeof(shareMem->execScript)); if (cmd && !strncmp(cmd, "#!", 2)) { FILE *fhnd; sprintf(shareMem->execScript, "/tmp/.execScript_%d", getpid()); unlink(shareMem->execScript); fhnd = fopen(shareMem->execScript, "w"); if (fhnd) { fputs(cmd, fhnd); fputs("\n", fhnd); fchmod(fileno(fhnd), 0755); fclose(fhnd); cmd = shareMem->execScript; } } /* execute command */ shareMem->errCode = RSRV_SUCCESS; // make sure it's been reset execle("/bin/csh", "csh", "-f", "-c", cmd, (char*)nil, env); shareMem->errCode = RSRV_EXEC; _exit(0); } /* set io */ if (cmdChild == -1) { close(myInput); myInput = -1; } close(hisOutput); return myInput; } /* thread to wait for command completion */ - (void*)_waitForComplete { /* read data if inputDescriptor is valid */ if (inputDescriptor >= 0) { int cnt; char buffer[1025]; do { cnt = read(inputDescriptor, buffer, 1024); if (cnt != -1) [server _commandOutput:buffer len:cnt execId:(u_int)self]; } while (cnt > 0); if ((exitErr=_waitForExit(cmdChild)) < 0) shareMem->errCode = RSRV_ABORTED; inputDescriptor = cmdChild = -1; // indicate that child is now inactive } else { shareMem->errCode = RSRV_FORK; // command never even had a chance } /* indicate command completion */ [server _commandDidComplete:(u_int)self]; return self; } /* thread router */ static void *_waitForComplete(id fakeSelf) { return (void*)[(RunCommand*)fakeSelf _waitForComplete]; } /* execute command */ - (void)_execCommand:(const char*)cmd user:(const char*)user:(const char*)pass { inputDescriptor = [self _pipeExec:cmd user:user:pass]; cthread_detach(cthread_fork((cthread_fn_t)_waitForComplete,self)); } /* terminate command (MAY be blocked) */ - (void)_terminateCommand { if (cmdChild > 0) { killpg(cmdChild, SIGTERM); kill(cmdChild, SIGTERM); } } /* kill command (CANNOT be blocked) */ - (void)_killCommand { if (cmdChild > 0) { killpg(cmdChild, SIGKILL); kill(cmdChild, SIGKILL); } } // ------------------------------------------------------------------------------------- // RunCommand: sender(client) connection is invalid - senderIsInvalid:sender { NXLogError("[RunCommand] Connection to client failed ..."); client = (id)nil; // clear client if (killOnError) [self _killCommand]; return self; } @end // ------------------------------------------------------------------------------------- // _ExecServerd implementation #define isMyCHILD(X) ((X) && ([childList indexOf:(id)(X)] != NX_NOT_IN_LIST)) @implementation _ExecServerd /* set exeServerOwner. (this is set in the child process only) */ + setExecServerOwner:theOwner { extern ExecServer *exeServerOwner; // ExecServer instance owner exeServerOwner = theOwner; return self; } /* init instance */ - init { [super init]; childList = [[[List alloc] initCount:1] empty]; serverName = (char*)nil; server = (id)nil; clientCount = 0; return self; } // ------------------------------------------------------------------------------------- // Root_process commands // ------------------------------------------------------------------------------------- /* remote: client initiated connection to server */ - (void)_pingServer { /* count active client */ clientCount++; } /* remote: return 'root' flag */ - (BOOL)_isRunningAsRoot { return (exeGetpwnam("root")->pw_uid == geteuid())? YES : NO; } /* remote: exec command */ - (execHandle_t)_runCommand:(const char*)command user:(const char*)userName:(const char*)password client:(id <RemoteClient>)theClient kill:(BOOL)killOnError { RunCommand *runCmd; /* create/initialize command executor */ runCmd = [[RunCommand alloc] init]; runCmd->killOnError = killOnError; runCmd->server = server; if (theClient) { NXConnection *conn = [(NXProxy*)theClient connectionForProxy]; runCmd->client = theClient; [(NXProxy*)theClient setProtocolForProxy:@protocol(RemoteClient)]; [[conn outPort] registerForInvalidationNotification:runCmd]; } /* keep track of all children */ [childList addObject:runCmd]; /* set command to execute */ [runCmd _execCommand:command user:userName:password]; /* free arguments */ if (command) free((char*)command); if (userName) free((char*)userName); if (password) free((char*)password); return (execHandle_t)runCmd; } /* remote: open pipe to shell and execute method */ - (int)_uperform:(SEL)method withArg:(const char*)arg user:(const char*)userName:(const char*)password { int child, exitErr; shareStat_t *share; extern ExecServer *exeServerOwner; // ExecServer instance owner /* allocate storage shared with child process */ share = (shareStat_t*)_share_alloc(sizeof(shareStat_t)); /* single pass loop */ for (;;) { id ms = exeServerOwner->methodDelegate; /* check for methServer response to method */ if (!ms || ![ms respondsTo:method]) { exitErr = RSRV_UNDEF; break; } /* fork and perform method */ if ((child = forkCHILD) == 0) { setpgrp(getpid(), getpid()); if (share->errCode = exeSwitchToUser(userName, password)) { sprintf(share->errMsg, "user=%s, pass=%s", userName, password); _exit(0); } _exit((int)[ms perform:method with:(id)arg] & 0xFF); } /* check for fork failure */ if (child == -1) { exitErr = RSRV_FORK; break; } /* wait for child to exit */ exitErr = _waitForExit(child); if (share->errCode) exitErr = share->errCode; break; } /* free arguments and return */ _share_free((void*)share, sizeof(shareStat_t)); if (arg) free((char*)arg); if (userName) free((char*)userName); if (password) free((char*)password); return exitErr; } /* terminate command */ - (void)_terminateCommand:(execHandle_t)runId { RunCommand *runCmd = (RunCommand*)runId; if (isMyCHILD(runCmd)) [runCmd _terminateCommand]; } /* kill command */ - (void)_killCommand:(execHandle_t)runId { RunCommand *runCmd = (RunCommand*)runId; if (isMyCHILD(runCmd)) [runCmd _killCommand]; } /* return true if child is still active */ - (BOOL)_childIsActive:(execHandle_t)runId { RunCommand *runCmd = (RunCommand*)runId; return (isMyCHILD(runCmd))? YES : NO; } /* shut down server */ - (void)_shutDownServer { int i; extern ExecServer *exeServerOwner; // ExecServer instance owner exeServerOwner->exitWhenDone = YES; for (i = 0; i < [childList count]; i++) { RunCommand *runId = (RunCommand*)[childList objectAt:i]; [runId _killCommand]; } } // ------------------------------------------------------------------------------------- // internal Root_process notification // ------------------------------------------------------------------------------------- - (void)_commandOutput:(const char*)buff len:(int)len execId:(u_int)fakeRun { RunCommand *runCmd = (RunCommand*)fakeRun; /* send text output to client */ if (runCmd->client) [runCmd->client commandOutput:buff len:len]; /* free buffer */ free((char*)buff); } - (void)_commandDidComplete:(u_int)fakeRun { RunCommand *runCmd = (RunCommand*)fakeRun; int err; extern BOOL _debugMode; // debug mode extern ExecServer *exeServerOwner; // ExecServer instance owner /* debug message */ if (_debugMode && *(runCmd->shareMem->errMsg)) { // debuging purposes NXLogError("[_ExecServerd] (0x%X) %s", runCmd->shareMem->errCode, runCmd->shareMem->errMsg); } /* message client */ err = runCmd->shareMem->errCode?runCmd->shareMem->errCode:runCmd->exitErr; if (runCmd->client) [runCmd->client commandDidCompleteWithError:err]; /* unregister runCmd */ if (runCmd->client) { NXPort *port = [[(NXProxy*)runCmd->client connectionForProxy] outPort]; [port unregisterForInvalidationNotification:runCmd]; } [NXConnection unregisterForInvalidationNotification:runCmd]; /* remove script wrapper, if any */ if (*(runCmd->shareMem->execScript)) unlink(runCmd->shareMem->execScript); /* free resources */ [NXConnection removeObject:runCmd]; [childList removeObject:runCmd]; [runCmd free]; /* check for exit */ if (!clientCount && ([childList count] <= 0) && exeServerOwner->exitWhenDone) { NXLogError("[_ExecServerd] Server terminating..."); _exit(0); } } // ------------------------------------------------------------------------------------- // external notification // ------------------------------------------------------------------------------------- /* from connection startup notification */ - connection:(NXConnection*)conn didConnect:(NXConnection*)newConn { [[newConn outPort] registerForInvalidationNotification:self]; return newConn; } /* server: mainApp(client) connection went bad (client died?) */ - senderIsInvalid:sender { /* decrement client count */ clientCount--; /* terminate now if no children */ if (!clientCount && ([childList count] <= 0)) { NXLogError("[_ExecServerd] Server terminating..."); _exit(0); } return self; } @end // ************************************************************************************* // ************************************************************************************* // ExecServer implementation // ************************************************************************************* // ************************************************************************************* /* print already running error */ #define IS_RUNNING (isRunning? _alreadyRunning(self,_cmd) : 0) static int _alreadyRunning(id self, SEL _cmd) { NXLogError("[ExecServer] Attempt to set attribute after server has started"); return 1; } @implementation ExecServer // ------------------------------------------------------------------------------------- // ExecServer class methods // ------------------------------------------------------------------------------------- /* return error code description */ typedef struct { int err; char *desc; } _shellError_t; static _shellError_t _shellErrors[] = { { RSRV_ABORTED, "Command Aborted" }, { RSRV_BADPASSWD, "Invalid User Password" }, { RSRV_BADUSER, "Invalid User Name" }, { RSRV_BADGID, "setgid() Error: Permission Denied" }, { RSRV_BADUID, "setuid() Error: Permission Denied" }, { RSRV_BADINIT, "initgroups() Error: Permission Denied" }, { RSRV_EXEC, "Unable to Execute Command" }, { RSRV_RSH, "Remote rsh exec() failed" }, { RSRV_FORK, "Unable to Create New Process" }, { RSRV_UNKNOWN, "Unknown Error" }, { RSRV_UNDEF, "Undefined target/process" }, { RSRV_SUCCESS, "Command Completed Successfully" }, // must be last { 0, (char*)nil } }; static char *_errorDesc(int err) { _shellError_t *e; for (e = _shellErrors; e->desc && (err != e->err); e++); return e->desc; } + (char*)errorDesc:(int)err { return _errorDesc(err); } /* cache uids */ + (void)_cacheUid { struct passwd *pw; extern uid_t exeUserUid; // login user uid extern uid_t exeRootUid; // root user uid extern struct passwd *exeGetpwnam(const char *user); /* get 'root' uid */ if (!(pw = exeGetpwnam("root"))) { NXLogError("[ExecServer] exeGetpwnam(\"root\") failed"); exit(1); } exeRootUid = pw->pw_uid; /* get login uid */ if (!(pw = exeGetpwnam((char*)nil))) { NXLogError("[ExecServer] Unknown login user name"); exit(1); } exeUserUid = pw->pw_uid; } // ------------------------------------------------------------------------------------- // ExecServer initialization // ------------------------------------------------------------------------------------- /* ExecServer initialization */ - init { char localHostName[MAXHOSTNAMELEN + 1]; [super init]; isRunning = NO; exitWhenDone = YES; rootServer = (id)nil; methodDelegate = (id)nil; memset(mainAppPath, 0, sizeof(mainAppPath)); memset(mainAppHost, 0, sizeof(mainAppHost)); memset(mainAppServerName, 0, sizeof(mainAppServerName)); memset(remoteHost, 0, sizeof(remoteHost)); gethostname(localHostName, sizeof(localHostName)); sprintf(remoteServerName, "ExecServer_%s_%d", localHostName, getpid()); memset(serverCommandName, 0, sizeof(serverCommandName)); return self; } - free { // not yet implemented return [super free]; } // ------------------------------------------------------------------------------------- // ExecServer attributes // ------------------------------------------------------------------------------------- /* set exit when done flag (NOTE: MUST be called BEFORE startServer!) */ - setExitWhenDone:(BOOL)flag { exitWhenDone = flag; return self; } /* set method delegate class (NOTE: MUST be called BEFORE startServer!) */ - setMethodDelegate:classId { if (IS_RUNNING) return (id)nil; methodDelegate = (classId == [classId class])? classId : (id)nil; return methodDelegate; } /* set Main app path */ - setMainAppPath:(const char*)appPath { if (IS_RUNNING) return (id)nil; if (appPath) strcpy(mainAppPath, appPath); else *mainAppPath = 0; return self; } /* set remote server name used in environment (NOTE: MUST be called BEFORE startServer!) */ - setMainAppServerName:(const char*)servName host:(const char*)hostName { if (IS_RUNNING) return (id)nil; if (!servName || !*servName) *mainAppServerName = 0; else { strncpy(mainAppServerName, servName, sizeof(mainAppServerName)); if (!hostName || !*hostName) *mainAppHost = 0; else strncpy(mainAppHost, hostName, sizeof(mainAppHost)); } return self; } /* return main app server name */ - (const char*)mainAppServerName { return mainAppServerName; } /* return main app host name */ - (const char*)mainAppHost { return mainAppHost; } /* set remote host name (NOTE: MUST be called BEFORE startServer!) */ - setRemoteHost:(const char*)hostName { if (IS_RUNNING) return (id)nil; if (!hostName) *remoteHost = 0; else strncpy(remoteHost, hostName, sizeof(remoteHost)); return self; } /* return remote server name */ - (const char*)remoteHost { return remoteHost; } /* set name of remote ExecServer (NOTE: MUST be called BEFORE startServer!) */ - setRemoteServerName:(const char*)serverName { if (IS_RUNNING) return (id)nil; strncpy(remoteServerName, serverName, MAXHOSTNAMELEN); return self; } /* return remote runServer name */ - (const char*)remoteServerName { return remoteServerName; } /* set RemoteExecServer command path/name (NOTE: MUST be called BEFORE startServer!) */ - setServerCommandName:(const char*)cmdPath { if (IS_RUNNING) return (id)nil; if (!cmdPath) *serverCommandName = 0; else { struct stat st; if (stat((char*)cmdPath,&st)) return (id)nil; strncpy(serverCommandName, cmdPath, sizeof(serverCommandName)); } return self; } // ------------------------------------------------------------------------------------- // ExecServer startup /* ExecServer mainline (does not return) */ - (void)_runServer { _ExecServerd *rootId; NXConnection *connection; extern uid_t exeRootUid; // root user uid extern struct passwd *exeGetpwuid(uid_t uid); /* (re)cache uids */ [_ExecServerd setExecServerOwner:self]; [ExecServer _cacheUid]; /* set process group */ setpgrp(getpid(), getpid()); /* init root process */ if (exeRootUid == geteuid()) { int err = 0; struct passwd *pw; if (!(pw = exeGetpwuid(exeRootUid))) err = RSRV_BADUSER; else if (setgid(pw->pw_gid) < 0) err = RSRV_BADGID; else if (initgroups(pw->pw_name, pw->pw_gid)) err = RSRV_BADINIT; else if (setuid(exeRootUid) < 0) err = RSRV_BADUID; if (err) { NXLogError("[ExecServer] Server User 'root': %s", _errorDesc(err)); _exit(1); } } /* start server */ // NXLogError("[ExecServer] starting %s", remoteServerName); rootId = [[_ExecServerd alloc] init]; connection = [NXConnection registerRoot:rootId withName:remoteServerName]; rootId->serverName = STRCOPY(remoteServerName); rootId->server = (id)[NXConnection connectToPort:[connection inPort]]; [(NXProxy*)rootId->server setProtocolForProxy:@protocol(RootInternal)]; [NXPort worryAboutPortInvalidation]; [connection registerForInvalidationNotification:rootId]; [connection setDelegate:rootId]; [connection run]; _exit(0); } /* connect to server */ - _connectToServer:(int)tries { /* establish connection to server, retry until successful */ if (!rootServer) { int i; for (i = tries;;) { rootServer = (_ExecServerd*)[NXConnection connectToName:remoteServerName onHost:(*remoteHost?remoteHost:(char*)nil)]; if (rootServer || (--i <= 0)) break; sleep(1); } } /* return connection */ return (_ExecServerd*)rootServer; } /* start root server (MUST be called only ONCE at the beginning of the application) */ - startServer { NXConnection *connection; int err; extern int exeSwitchToUser(const char *user, const char *pass); /* check for already running */ if (isRunning) return self; /* set timeout defaults */ [NXConnection setDefaultTimeout:-1]; /* (re)cache uids */ [ExecServer _cacheUid]; /* start up server */ if (*remoteHost) { if (![self _connectToServer:1]) { /* start ExecServer process */ if ((rootChild = forkCHILD) == 0) { char cmd[2048], *c = cmd; setpgrp(getpid(), getpid()); sprintf(c, "%s -r %s", serverCommandName, remoteServerName); c += strlen(c); if (exitWhenDone) { sprintf(c, " -e"); c += strlen(c); } if (*mainAppPath) { sprintf(c, " -c %s", mainAppPath); c += strlen(c); } if (*mainAppServerName) { sprintf(c, " -m %s", mainAppServerName); c += strlen(c); if (*mainAppHost) { sprintf(c, " %s", mainAppHost); c += strlen(c); } } execl("/usr/ucb/rsh", "/usr/ucb/rsh", remoteHost, "-n", cmd, NULL); NXLogError("[ExecServer] %s", _errorDesc(RSRV_RSH)); exit(1); // child process termination error } /* connect to server and return */ if (![self _connectToServer:12]) { NXLogError("[ExecServer] Cannot connect to server '%s' on '%s'", remoteServerName, remoteHost); killpg(rootChild, SIGKILL); kill(rootChild, SIGKILL); return (id)nil; } } else { NXLogError("[ExecServer] Connected to existing server '%s' on '%s'", remoteServerName, remoteHost); } } else { /* start ExecServer process */ if ((rootChild = forkCHILD) == 0) { setpgrp(getpid(), getpid()); [self _runServer]; exit(1); // child process termination error } /* connect to server and return */ if (![self _connectToServer:7]) { NXLogError("[ExecServer] Cannot connect to local server '%s'", remoteServerName); killpg(rootChild, SIGKILL); kill(rootChild, SIGKILL); return (id)nil; } } /* connection successful */ isRunning = YES; /* switch to normal login user (password not required) */ if (err = exeSwitchToUser((char*)nil, (char*)nil)) { NXLogError("[ExecServer] login user: %s", _errorDesc(err)); return (id)nil; } /* initialize server connection */ connection = [(NXProxy*)rootServer connectionForProxy]; [(NXProxy*)rootServer setProtocolForProxy:@protocol(RunService)]; [connection registerForInvalidationNotification:self]; [connection runFromAppKit]; [(_ExecServerd*)rootServer _pingServer]; /* return successful */ return self; } // ------------------------------------------------------------------------------------- // password check // ------------------------------------------------------------------------------------- /* check user password flag */ - (BOOL)needUserPassword:(const char*)user { struct passwd *pw; extern uid_t exeUserUid; // login user uid extern struct passwd *exeGetpwnam(const char *user); if (!(pw = exeGetpwnam(user))) return YES; // invalid user if (pw->pw_uid == exeUserUid) return NO; return YES; } // ------------------------------------------------------------------------------------- // ExecServer services // ------------------------------------------------------------------------------------- - (BOOL)isRunningAsRoot { BOOL isRoot; if (!rootServer) return NO; NX_DURING isRoot = [(_ExecServerd*)rootServer _isRunningAsRoot]; NX_HANDLER isRoot = NO; NX_ENDHANDLER return isRoot; } /* issue run command */ - (execHandle_t)runCommand:(const char*)cmd withUser:(const char*)userName:(const char*)password forClient:(id <RemoteClient>)client killOnError:(BOOL)killOnError { execHandle_t runId; if (!rootServer) return (execHandle_t)nil; NX_DURING runId = [(_ExecServerd*)rootServer _runCommand:cmd user:userName:password client:client kill:killOnError]; NX_HANDLER runId = (execHandle_t)nil; NX_ENDHANDLER return runId; } /* issue run command */ - (execHandle_t)runCommand:(const char*)cmd forClient:(id <RemoteClient>)client killOnError:(BOOL)killOnError { return [self runCommand:cmd withUser:(char*)nil:(char*)nil forClient:client killOnError:killOnError]; } /* terminate command */ - terminateCommand:(execHandle_t)runId { id rtn = self; if (!rootServer) return (id)nil; NX_DURING [(_ExecServerd*)rootServer _terminateCommand:(execHandle_t)runId]; NX_HANDLER rtn = (id)nil; NX_ENDHANDLER return rtn; } /* kill command (unmaskable) */ - killCommand:(execHandle_t)runId { id rtn = self; if (!rootServer) return (id)nil; NX_DURING [(_ExecServerd*)rootServer _killCommand:(execHandle_t)runId]; NX_HANDLER rtn = (id)nil; NX_ENDHANDLER return rtn; } /* returns true if command handle is still valid */ - (BOOL)commandIsActive:(execHandle_t)runId { BOOL rtn = NO; if (!rootServer) return NO; NX_DURING rtn = [(_ExecServerd*)rootServer _childIsActive:(execHandle_t)runId]; NX_HANDLER rtn = NO; NX_ENDHANDLER return rtn; } /* shut down _ExecServerd */ - shutDownServer { id rtn = self; if (!rootServer) return self; NX_DURING [(_ExecServerd*)rootServer _shutDownServer]; NX_HANDLER rtn = (id)nil; NX_ENDHANDLER return rtn; } // ------------------------------------------------------------------------------------- /* perform method from server */ - (int)perform:(SEL)method withArg:(const char*)arg withUser:(const char*)userName:(const char*)password; { int rtn; if (!rootServer) return RSRV_UNDEF; NX_DURING rtn = (int)[(_ExecServerd*)rootServer _uperform:method withArg:arg user:userName:password]; NX_HANDLER rtn = RSRV_UNKNOWN; NX_ENDHANDLER return rtn; } // ------------------------------------------------------------------------------------- // failure notification // ------------------------------------------------------------------------------------- /* mainApp: connection to server went bad (server died?) */ - senderIsInvalid:sender { rootServer = (id)nil; // ?????? NXLogError("[ExecServer] Server port went bad!"); _exit(1); return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.