summaryrefslogtreecommitdiffstats
path: root/src/rpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc.c')
-rw-r--r--src/rpc.c132
1 files changed, 132 insertions, 0 deletions
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 <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#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 );
+}
+