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