summaryrefslogtreecommitdiffstats
path: root/src/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util.c')
-rw-r--r--src/util.c187
1 files changed, 187 insertions, 0 deletions
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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <pwd.h>
+#include <poll.h>
+
+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 );
+}
+