blob: 34276e9812e00ea95f1f8484bc91425941848eef [file] [log] [blame] [raw]
/*
* Copyright 2015-2018 Rivoreo
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include "common.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int server_mode(const char *home) {
struct sockaddr_un addr = { .sun_family = AF_UNIX };
size_t home_len = strlen(home);
if(home_len + 1 + sizeof SOCKET_NAME > sizeof addr.sun_path) {
fprintf(stderr, "home path too long (%zu bytes)\n", home_len);
return 1;
}
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(fd == -1) {
perror("socket");
return 1;
}
if(chdir(home) < 0) perror(home);
//char socket_path[home_len + 1 + sizeof SOCKET_NAME];
memcpy(addr.sun_path, home, home_len);
addr.sun_path[home_len] = '/';
memcpy(addr.sun_path + home_len + 1, SOCKET_NAME, sizeof SOCKET_NAME);
unlink(addr.sun_path);
if(bind(fd, (struct sockaddr *)&addr, sizeof addr) < 0) {
perror(addr.sun_path);
close(fd);
return 1;
}
if(fchmod(fd, 0600) < 0) {
perror("fchmod");
close(fd);
unlink(addr.sun_path);
return 1;
}
if(listen(fd, 64) < 0) {
perror("listen");
close(fd);
unlink(addr.sun_path);
return 1;
}
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
int maxfd = fd;
int client_fds[FD_SETSIZE];
int i;
for(i=0; i<FD_SETSIZE; i++) client_fds[i] = -1;
while(1) {
fd_set rfdset = fdset;
int n = select(maxfd + 1, &rfdset, NULL, NULL, NULL);
if(n < 0) {
if(errno == EINTR) continue;
perror("select");
return 1;
}
if(FD_ISSET(fd, &rfdset)) {
struct sockaddr_un client_addr;
socklen_t addr_len = sizeof client_addr;
int cfd;
do {
cfd = accept(fd, (struct sockaddr *)&client_addr, &addr_len);
} while(cfd == -1 && errno == EINTR);
if(cfd == -1) {
perror("accept");
//if(errno == EMFILE) continue;
//return 1;
//continue;
if(errno == EMFILE && n < 2) sleep(1);
} else {
fprintf(stderr, "client fd %d\n", cfd);
/*
for(i = 0; client_fds[i] != -1; i++) {
}
*/
i = 0;
while(1) {
if(i >= FD_SETSIZE) {
fprintf(stderr, "warning: cannot add fd %d to set, too many clients\n", cfd);
close(cfd);
break;
}
if(client_fds[i] == -1) {
client_fds[i] = cfd;
FD_SET(cfd, &fdset);
if(cfd > maxfd) maxfd = cfd;
fprintf(stderr, "client %d fd %d\n", i, cfd);
break;
}
i++;
}
}
n--;
}
for(i=0; n && i<FD_SETSIZE; i++) {
int cfd = client_fds[i];
if(cfd == -1) continue;
if(FD_ISSET(cfd, &rfdset)) {
n--;
// TODO: read msg
}
}
}
}