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. There will be warnings under NeXSTEP3.0. 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 * 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.h> #import <mach_error.h> #import <dpsclient/dpsclient.h> #import <sys/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 use 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? */ 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.