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.