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/rpc.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 src/rpc.c (limited to 'src/rpc.c') diff --git a/src/rpc.c b/src/rpc.c new file mode 100644 index 0000000..e49547e --- /dev/null +++ b/src/rpc.c @@ -0,0 +1,132 @@ +#include "rpc.h" +#include "util.h" +#include "main.h" +#include "userlist.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCKPATH "/run/idle-daemon" + +static void handleClient( int fd, struct ucred *user ); + +int rpcOpen() +{ + int fd = socket( AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0 ); + if ( fd == -1 ) { + perror( "Cannot create local RPC socket" ); + return -1; + } + struct sockaddr_un address = { + .sun_family = AF_UNIX, + .sun_path = SOCKPATH, + }; + unlink( SOCKPATH ); + if ( bind( fd, (struct sockaddr *)&address, sizeof(address) ) == -1 ) { + perror( "Could not bind RPC socket" ); + close( fd ); + return -1; + } + chmod( SOCKPATH, 0777 ); + if ( listen( fd, 10 ) == -1 ) { + perror( "Could not listen() on RPC socket" ); + close( fd ); + return -1; + } + return fd; +} + +void rpcWait( int listenFd, int seconds ) +{ + waitRead( listenFd, seconds * 1000 ); +} + +void rpcHandle( int listenFd ) +{ + int fd; + while ( ( fd = accept( listenFd, NULL, NULL ) ) != -1 ) { + // Determine who connected + socklen_t len; + struct ucred ucred; + len = sizeof(struct ucred); + if ( getsockopt( fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len ) == -1 ) { + perror( "Could not get credentials of connection" ); + close( fd ); // TODO: Allow more for root (eg. cancel reboot/poweroff) + continue; + } + if ( ! doublefork() ) + continue; // Parent continues + handleClient( fd, &ucred ); + exit( 0 ); + } + if ( errno != EAGAIN && errno != EWOULDBLOCK ) { + perror( "accept() on RPC socket failed" ); + } +} + +static void handleClient( int fd, struct ucred *user ) +{ + //printf( "Credentials from SO_PEERCRED: pid=%ld, euid=%ld, egid=%ld\n", + // (long) ucred.pid, (long) ucred.uid, (long) ucred.gid ); + // Make socket blocking (should be default on linux after accept() but...) + (void)user; + int flags = fcntl( fd, F_GETFL, 0 ); + if ( flags != -1 ) { + fcntl( fd, F_SETFL, flags & ~O_NONBLOCK ); + } + // But set timeouts + struct timeval tv = { + .tv_sec = 2, + }; + setsockopt( fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv) ); + setsockopt( fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv) ); + // Now read request + char buffer[1000]; + ssize_t len = read( fd, buffer, sizeof(buffer) - 1 ); + if ( len <= 0 ) + return; + buffer[len] = '\0'; + if ( strncmp( buffer, "get", 3 ) != 0 ) { + write( fd, "error", 5 ); + } else { + // Get request + FILE *s = fdopen( fd, "w" ); + if ( s == NULL ) { + perror( "Cannot wrap socket in stream" ); + return; + } + // 1) Global state + time_t deadline; + const char *name = "none"; + main_getStatus( &name, &deadline ); + fprintf( s, "[General]\n" + "nextAction=%s\n" + "nextActionTime=%lld\n", + name, (long long)deadline ); + // 2) Requested sessions + char *tok = strtok( buffer + 4, " \t\n\r" ); + while ( tok != NULL ) { + struct user *user = main_getUser( tok ); + if ( user != NULL ) { + fprintf( s, "[%s]\n" + "logoutTime=%lld\n" + "locked=%d\n", + tok, (long long)user->logoutTime, (int)user->isLocked ); + } + tok = strtok( NULL, " \t\n\r" ); + } + fflush( s ); + } + shutdown( fd, SHUT_WR ); + read( fd, buffer, sizeof(buffer) ); + close( fd ); +} + -- cgit v1.2.3-55-g7522