summaryrefslogtreecommitdiffstats
path: root/src/rpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc.c')
-rw-r--r--src/rpc.c304
1 files changed, 206 insertions, 98 deletions
diff --git a/src/rpc.c b/src/rpc.c
index e49547e..bfcc2c3 100644
--- a/src/rpc.c
+++ b/src/rpc.c
@@ -4,7 +4,7 @@
#include "userlist.h"
#include <poll.h>
-#include <sys/types.h>
+#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
@@ -14,119 +14,227 @@
#include <unistd.h>
#include <stdlib.h>
+#define SENDSTRING(fd,x) write( fd, x, strlen(x) )
+
#define SOCKPATH "/run/idle-daemon"
+#pragma pack(1)
+static struct {
+ int read;
+ int write;
+} rpcPipe = {
+ .read = -1,
+ .write = -1,
+};
+#pragma pack(0)
+
+_Static_assert( sizeof(rpcPipe) == ( sizeof(int) * 2 ), "Structsize mismatch" );
+
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;
+ 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;
+ }
+ if ( rpcPipe.write == -1 && rpcPipe.read == -1 ) {
+ if ( pipe2( (int*)&rpcPipe, O_DIRECT ) == -1 ) {
+ perror( "Cannot create local RPC pipe" );
+ } else {
+ // Read end nonblocking
+ int flags = fcntl( rpcPipe.read, F_GETFL, 0 );
+ fcntl( rpcPipe.read, F_SETFL, flags | O_NONBLOCK );
+ }
+ }
+ return fd;
}
void rpcWait( int listenFd, int seconds )
{
- waitRead( listenFd, seconds * 1000 );
+ struct pollfd pd[2] = {
+ { .fd = listenFd, .events = POLLIN | POLLHUP | POLLRDHUP },
+ { .fd = rpcPipe.read, .events = POLLIN | POLLHUP | POLLRDHUP },
+ };
+ if ( poll( pd, 2, seconds * 1000 ) == -1 ) {
+ perror( "Error polling RPC sockets" );
+ }
}
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" );
- }
+ 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" );
+ }
+ // Check if any RPC client talks to us
+ char buffer[200];
+ ssize_t len;
+ while ( ( len = read( rpcPipe.read, buffer, sizeof(buffer) - 1 ) ) > 0 ) {
+ buffer[len] = '\0';
+ const char *ptr = NULL;
+ enum Shutdown action = SHUTDOWN_ENUM_END;
+ if ( strncmp( buffer, "reboot ", 7 ) == 0 ) {
+ ptr = buffer + 7;
+ action = REBOOT;
+ } else if ( strncmp( buffer, "poweroff ", 9 ) == 0 ) {
+ ptr = buffer + 9;
+ action = POWEROFF;
+ } else if ( strncmp( buffer, "kexec ", 6 ) == 0 ) {
+ ptr = buffer + 6;
+ action = KEXEC;
+ } else {
+ fprintf( stderr, "Unknown RPC pipe callback: %s\n", buffer );
+ }
+ if ( ptr != NULL ) {
+ char *end;
+ int seconds = strtol( ptr, &end, 10 );
+ if ( end != ptr && seconds >= 0 ) {
+ main_queueAction( action, seconds );
+ } else {
+ fprintf( stderr, "RPC: Ignoring reboot/poweroff with invalid timeout\n" );
+ }
+ }
+ }
+ if ( len == -1 && errno != EAGAIN && errno != EWOULDBLOCK ) {
+ perror( "Error reading vom RPC local pipe" );
+ }
+}
+
+static void handleClient( int fd, struct ucred *creds )
+{
+ //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...)
+ 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 ) {
+ // 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 );
+ // Don't fclose since we need to shutdown below. This is a leak, but since we're
+ // running in a child that will exit soon, just close your eyes for a second...
+ } else if ( strncmp( buffer, "reboot ", 7 ) == 0 || strncmp( buffer, "poweroff ", 9 ) == 0
+ || strncmp( buffer, "kexec ", 6 ) == 0 ) {
+ if ( creds->uid != 0 ) {
+ SENDSTRING( fd, "error only root can do this" );
+ } else {
+ if ( write( rpcPipe.write, buffer, len ) == -1 ) {
+ perror( "RPC Child: Cannot write to parent pipe" );
+ SENDSTRING( fd, "error cannot write to parent" );
+ } else {
+ SENDSTRING( fd, "ok" );
+ }
+ }
+ } else {
+ SENDSTRING( fd, "error unknown command" );
+ }
+ shutdown( fd, SHUT_WR );
+ read( fd, buffer, sizeof(buffer) );
+ close( fd );
}
-static void handleClient( int fd, struct ucred *user )
+bool rpc_send( const char *data )
{
- //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 );
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX,
+ .sun_path = SOCKPATH,
+ };
+ int fd = socket( AF_UNIX, SOCK_STREAM, 0 );
+ if ( fd == -1 ) {
+ perror( "RPC Client: socket() failed" );
+ return false;
+ }
+ if ( connect( fd, &addr, sizeof(addr) ) == -1 ) {
+ perror( "RPC Client: connect() failed" );
+ close( fd );
+ return false;
+ }
+ if ( write( fd, data, strlen( data ) ) <= 0 ) {
+ perror( "RPC Client: write() failed" );
+ close( fd );
+ return false;
+ }
+ shutdown( fd, SHUT_WR );
+ char buffer[200];
+ ssize_t len;
+ if ( ( len = read( fd, buffer, sizeof(buffer) - 1 ) ) == -1 ) {
+ perror( "RPC Client: read()ing reply failed" );
+ } else {
+ buffer[len] = '\0';
+ printf( "%s\n", buffer );
+ }
+ close( fd );
+ return true;
}