ftp.nice.ch/pub/next/tools/workspace/Stuart.2.6.3.NIHS.b.tar.gz#/Stuart2.6.3.NIHS/slog/slog.m

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(), &notify);
    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.