This is slog.m in view mode; [Download] [Up]
// Replacement for StuartLog in Stuart2.4. // // This program is in the public domain. Use and abuse it as you // see fit. // // This program requires setuid-root to run correctly. There's // now a Makefile that sets all of that up - so use make to compile // this. Of course, consult Stuart's online documentation under // Installation/slot for more information. // // // The StuartLog tool in Stuart2.3 was a good idea, but it didn't // quite work. I found that it would often simply hang during the // login process, thus either hanging Stuart or not getting the // logging functions done. slog takes a new approach. It runs // continuously from the time Stuart is launched, and the same // process handles all logging functions. Stuart communicates with // slog via a private Speaker/Listener pair. slog also monitors // the parent Stuart process and exits on abnormal termination. // // Admire the code to connect to the parent process. I think it // cost me a kidney. // // scott hess // scott@nic.gac.edu // shess@ssesco.com // #import "SLogListener.h" #import <objc/HashTable.h> #import <libc.h> #import <grp.h> #import <lastlog.h> #import <utmp.h> #import <ttyent.h> #import <pwd.h> #import <mach/mach.h> #import <mach/mach_error.h> #import <dpsclient/dpsclient.h> #import <mach/notify.h> @interface SLogger : Object { SLogListener *listener; HashTable *slots; int uid; const char *name; port_t parentNotify; } - run; @end // Locking open and close. Though flock() is not a good // general-purpose file locker due to NFS limitations. It // works well for this case since only the local machine can // access the devices. int lopen( const char *filename, int openFlags) { int fd=open( filename, openFlags); if( fd>-1) { flock( fd, LOCK_EX); } return fd; } int lclose( int fd) { flock( fd, LOCK_UN); return close( fd); } // Fix ownerships and permissions on the named pty line. void fixOwnership( const char *pty, int uid, int gid, int mod) { char dev[ 64]; sprintf( dev, "/dev/%s", pty); chown( dev, uid, gid); chmod( dev, mod); } // Write an entry to wtmp. void writeWtmp( struct utmp *ut) { int f=lopen( "/usr/adm/wtmp", O_WRONLY | O_APPEND); if( f>=0) { write( f, ut, sizeof( struct utmp)); lclose( f); } else { perror( "opening /usr/adm/wtmp"); } } // Write an entry to utmp. void writeUtmp( struct utmp *ut, int slot) { if( slot>-1) { int f=lopen( "/etc/utmp", O_WRONLY); if( f>=0) { lseek( f, slot*sizeof( struct utmp), L_SET); write( f, ut, sizeof( struct utmp)); lclose( f); } else { perror( "opening /etc/utmp"); } } } @implementation SLogger // Initialize uid to an invalid user id (0 is valid). - init { self=[super init]; if( self) { uid=-1; } return self; } // Find the slot in the /etc/ttys file for the given device. // Cache a mapping from the device name to the slot number for // future use. -(int)getSlot:(const char *)device { if( ![slots isKey:device]) { struct ttyent *t; int slot; setttyent(); for( slot=1; t=getttyent(); slot++) { if( !strcmp( device, t->ty_name)) { break; } } endttyent(); if( !t) { slot=-1; } if( !slots) { slots=[HashTable allocFromZone:[self zone]]; slots=[slots initKeyDesc:"*" valueDesc:"i" capacity:0]; } device=NXUniqueString( device); [slots insertKey:device value:(void *)slot]; return slot; } else { return (int)[slots valueForKey:device]; } } // Login a user on the given pty. -(int)login:(char *)pty ownerships:(int)ownership utmp:(int)utmp wtmp:(int)wtmp lastlog:(int)lastlog { // Cache a passwd entry for the user if needed. if( uid==-1) { // Grab a passwd entry, set up uid. This code was // suggested by der Mouse <mouse@larry.mcrcim.mcgill.edu> char *user=getenv( "USER"); struct passwd *pw=NULL; uid=getuid(); if( user) { pw=getpwnam( user); } if( !pw || (uid && (uid!=pw->pw_uid))) { pw=getpwuid( uid); } if( pw) { uid=pw->pw_uid; } if( pw) { name=NXUniqueString( pw->pw_name); } else { name="Unknown"; } } if( utmp || wtmp || lastlog) { struct utmp ut; // Clean up the utmp entry. bzero( &ut, sizeof( ut)); // Set up the ut_name field if necessary. if( wtmp || utmp) { strncpy( ut.ut_name, name, sizeof( ut.ut_name)); } // Setup the line and time. strncpy( ut.ut_line, pty, sizeof( ut.ut_line)); time( &( ut.ut_time)); // Log to lastlog as needed. if( lastlog) { int f=lopen( "/usr/adm/lastlog", O_WRONLY); if( f>=0) { struct lastlog llog; bzero( &llog, sizeof( llog)); llog.ll_time=ut.ut_time; strncpy( llog.ll_line, ut.ut_line, sizeof( llog.ll_line)); lseek( f, uid*sizeof( llog), L_SET); write( f, &llog, sizeof( llog)); lclose( f); } else { perror( "opening /usr/adm/lastlog"); } } // Log to utmp and wtmp as needed. if( utmp) { writeUtmp( &ut, [self getSlot:pty]); } if( wtmp) { writeWtmp( &ut); } } // If needed, set pty ownership to the new user, with // permissions set for owner read/write, group write. // Group ownership set to the tty group, if available. if( ownership) { struct group *gr=getgrnam( "tty"); fixOwnership( pty, uid, gr ? gr->gr_gid : -1, 0620); } return 0; } -(int)login:(char *)pty ownerships:(int)ownership utmp:(int)utmp { #if 0 return [self login:pty ownerships:ownership utmp:utmp wtmp:utmp lastlog:utmp]; #else // This may make more sense. return [self login:pty ownerships:ownership utmp:utmp wtmp:YES lastlog:YES]; #endif } // This version has the return parameter okFlag which will // force Stuart to wait for slog to finish the operation before // continuing execution. -(int)login:(char *)pty ownerships:(int)ownership utmp:(int)utmp ok:(int *)okFlag { return [self login:pty ownerships:ownership utmp:utmp]; } -(int)logout:(char *)pty ownerships:(int)ownership utmp:(int)utmp wtmp:(int)wtmp lastlog:(int)lastlog { if( utmp || wtmp) { struct utmp ut; // Clean up the utmp entry. bzero( &ut, sizeof( ut)); strncpy( ut.ut_line, pty, sizeof( ut.ut_line)); time( &( ut.ut_time)); if( utmp) { writeUtmp( &ut, [self getSlot:pty]); } if( wtmp) { writeWtmp( &ut); } } // If needed, set pty ownership back to root user, with // permissions set for all read/write. Group ownership // reset to the tty group, if available. if( ownership) { struct group *gr=getgrnam( "tty"); fixOwnership( pty, 0, gr ? gr->gr_gid : -1, 0666); } return 0; } -(int)logout:(char *)pty ownerships:(int)ownership utmp:(int)utmp { #if 0 return [self logout:pty ownerships:ownership utmp:utmp wtmp:utmp lastlog:utmp]; #else // This may make more sense. return [self logout:pty ownerships:ownership utmp:utmp wtmp:YES lastlog:YES]; #endif } // I use this routine to let Stuart _kindly_ ask slog to exit. // I don't want Stuart doing a kill() on slog while slog's in // the middle of something ... -(void)exit { exit( 0); } // Disconnect our controlling tty and connect to the console // device. - ttyDisconnect { int tty; tty=open( "/dev/tty", O_RDWR); if( tty>-1) { ioctl( tty, TIOCNOTTY, 0); close( tty); } tty=open( "/dev/console", O_WRONLY); setpgrp( 0, getpid()); dup2( tty, 1); dup2( tty, 2); if( tty!=1 && tty!=2) { close( tty); } return self; } // Catch inadvertant parent process death. void notifyPortHandler( notification_t *msg, SLogger *self) { if( msg->notify_header.msg_id==NOTIFY_PORT_DELETED) { if( msg->notify_port==self->parentNotify) { [self exit]; } } } - run { kern_return_t ret; msg_header_t initMsg; extern int getppid( void); task_t parentTask; port_t notify; [self ttyDisconnect]; // Set up objects for our Listening pleasure. listener=[[SLogListener allocFromZone:[self zone]] init]; [listener setDelegate:self]; [listener usePrivatePort]; [listener addPort]; // Give us plenty of leeway for when people logout. // Actually, even this isn't really that great, but what // can you do? [I mean short of running multi-threaded // with one thread listening and the other thread handling // the log functions.] port_set_backlog( task_self(), [listener listenPort], PORT_BACKLOG_MAX); // Find our parent's notify port. ret=task_by_unix_pid( task_self(), getppid(), &parentTask); if( ret!=KERN_SUCCESS) { printf( "slog: Unable to get parent's task_t.\n"); exit( 1); } ret=task_get_notify_port( parentTask, &parentNotify); initMsg.msg_remote_port=parentNotify; if( ret!=KERN_SUCCESS) { printf( "slog: Unable to get parent's notify port.\n"); exit( 1); } port_allocate( task_self(), ¬ify); task_set_notify_port( task_self(), notify); DPSAddPort( notify, (void *)notifyPortHandler, 64, self, 31); // Set up the rest of the header. initMsg.msg_simple=TRUE; initMsg.msg_size=sizeof( initMsg); initMsg.msg_type=MSG_TYPE_NORMAL; initMsg.msg_id=0; // Including the port which our Listener listens on. initMsg.msg_local_port=[listener listenPort]; // Send it, and if successful, enter the event loop. ret=msg_send( &initMsg, SEND_TIMEOUT, 30000); if( ret==KERN_SUCCESS) { [Listener run]; } printf( "slog: Unable to send Listener port to parent.\n"); exit( 1); return self; } @end void main( void) { SLogger *logger=[[SLogger alloc] init]; [logger run]; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.