From f7cd45464fd7c037f6a60098ae77760998b3b4b6 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 10 May 2019 15:25:43 +0200 Subject: Initial commit --- src/util.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 src/util.c (limited to 'src/util.c') diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..e9d642c --- /dev/null +++ b/src/util.c @@ -0,0 +1,187 @@ +#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() +{ + 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() +{ + 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() +{ + 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 ); + sleep( 5 ); + if ( kill( user->sessionLeader, 0 ) == -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 ); +} + -- cgit v1.2.3-55-g7522