summaryrefslogblamecommitdiffstats
path: root/src/rpc.c
blob: e49547e6f801e9a09f76405cd1eee97208a8870c (plain) (tree)



































































































































                                                                                                        
#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 );
}