ftp.nice.ch/pub/next/text/tex/apps/TeXmenu5.0pre-English.NIHS.b.tar.gz#/TeXmenu5.0/Src/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.  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(), &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.