#include "util.h" #include "userlist.h" #include #include #include #include #include #include #include #include #include #include #include #include int _testmode = 0; // Base time for monotonic clock static struct timespec basetime; time_t now( void ) { struct timespec retval; if ( clock_gettime( CLOCK_MONOTONIC, &retval ) == -1 ) { perror( "Cannot clock_gettime CLOCK_MONOTONIC" ); abort(); } return (time_t)( retval.tv_sec - basetime.tv_sec ); } void init_time( void ) { if ( clock_gettime( CLOCK_MONOTONIC, &basetime ) == -1 ) { perror( "cannot clock_gettime CLOCK_MONOTONIC" ); basetime.tv_sec = 0; // Hope it doesn't overflow } } /** * Return false = parent, true = second child */ bool doublefork( void ) { const pid_t c1 = fork(); if ( c1 == -1 ) { perror( "doublefork: First fork failed" ); return false; } if ( c1 != 0 ) { // Parent (main program) int ws; while ( waitpid( c1, &ws, 0 ) == -1 && errno == EINTR ) {} return false; } // This is executed by first child signal( SIGCHLD, SIG_IGN ); // Fork again const pid_t c2 = fork(); if ( c2 == -1 ) { perror( "doublefork: Second fork failed" ); abort(); } if ( c2 != 0 ) { // Second parent (first child), exit immediately exit( 0 ); } // Second child, this is what we want signal( SIGCHLD, SIG_DFL ); return true; } bool waitRead( int fd, int ms ) { struct pollfd pd = { .fd = fd, .events = POLLIN | POLLHUP | POLLRDHUP }; return poll( &pd, 1, ms ) > 0 && ( pd.revents & POLLIN ) != 0; } void copy( const char *src, char *dst, size_t len, const char *stop ) { const char *end = src + len - 1; while ( src < end && *src != '\0' && ( stop == NULL || strchr( stop, *src ) == NULL ) ) { *dst++ = *src++; } *dst = '\0'; } void killSession( const struct user * user ) { if ( _testmode ) { printf( "Not really killing session %s of user %s.\n", user->sessionName, user->user ); return; } if ( ! doublefork() ) return; // Async do NOT use return from here on if ( user->sessionName[0] != '\0' ) { printf( "Terminating session %s of %s.\n", user->sessionName, user->user ); run( true, "loginctl", "terminate-session", user->sessionName, (char*)NULL ); sleep( 2 ); if ( kill( user->sessionLeader, SIGTERM ) == -1 || user->sessionHead == 0 || kill( user->sessionHead, 0 ) == -1 ) exit( 0 ); } if ( user->sessionHead != 0 && kill( user->sessionHead, SIGTERM ) == 0 ) { printf( "Session %s seems not entirely dead, TERMing loginpid %d...\n", user->sessionName, (int)user->sessionHead ); sleep( 2 ); } if ( ( killpg( user->sessionLeader, SIGTERM ) & kill( user->sessionLeader, SIGTERM ) ) == 0 ) { printf( "Session %s seems not entirely dead, TERMing process group %d...\n", user->sessionName, (int)user->sessionLeader ); sleep( 3 ); } // Maybe we need KILL now... if ( user->sessionHead != 0 && kill( user->sessionHead, SIGKILL ) == 0 ) { printf( "Session %s seems not entirely dead, KILLing %d...\n", user->sessionName, (int)user->sessionHead ); } exit( 0 ); } void redirect( int newIn, int newOut ) { if ( newIn > 2 ) { dup2( newIn, STDIN_FILENO ); close( newIn ); } if ( newOut > 2 ) { dup2( newOut, STDOUT_FILENO ); close( newOut ); } } void switchUserSafe( const char* user ) { errno = 0; struct passwd *u = getpwnam( user ); if ( u == NULL ) { if ( errno != 0 ) { perror( "switchUserSafe: Cannot switch to user" ); } else { fprintf( stderr, "switchUserSafe: Cannot switch to user %s: User not known.\n", user ); } exit( 1 ); } chdir( "/" ); if ( setgid( u->pw_gid ) != 0 ) { perror( "switchUserSafe: setgid failed" ); exit( 1 ); } if ( setuid( u->pw_uid ) != 0 ) { perror( "switchUserSafe: setuid failed" ); exit( 1 ); } if ( u->pw_dir == NULL ) { unsetenv( "HOME" ); } else { setenv( "HOME", u->pw_dir, 1 ); } setenv( "USER", u->pw_name, 1 ); } void run( bool detach, const char *file, ... ) { if ( detach && ! doublefork() ) return; char *argv[100]; argv[0] = strdup( file ); argv[99] = NULL; va_list ap; va_start( ap, file ); for ( int i = 1; i < 99; ++i ) { argv[i] = va_arg( ap, char* ); if ( argv[i] == NULL ) break; argv[i] = strdup( argv[i] ); } va_end( ap ); /* printf( "Running: '%s'", file ); for ( int i = 0; i < 100 && argv[i] != NULL; ++i ) { printf( " '%s'", argv[i] ); } printf( "\n" ); */ execvp( strdup( file ), argv ); // Something went wrong... perror( "run execvp failed" ); exit( 1 ); }