tel/tel.c

255 lines
5.1 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <signal.h>
#include <netdb.h>
#include <fcntl.h>
#include "libtel.h"
void handle_call(int sock) {
fcntl(0, F_SETFL, O_NONBLOCK);
fcntl(sock, F_SETFL, O_NONBLOCK);
struct pollfd fds[2];
fds[0].fd = 0;
fds[0].events = POLLIN | POLLERR | POLLHUP;
fds[1].fd = sock;
fds[1].events = POLLIN | POLLERR | POLLHUP;
while(1) {
char buf[1024];
int res = poll(fds, 2, -1);
if(res < 0) {
perror("error polling");
goto end_err;
}
if(fds[0].revents & POLLIN) {
while((res = read(0, buf, 1024)) > 0) {
res = tel_write_all(sock, buf, res);
if(res < 0) {
perror("failed to send message");
goto end_err;
}
}
if(res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
perror("failed to read input");
goto end_err;
}
}
if(fds[1].revents & POLLIN) {
int readcount = 0;
while((res = read(sock, buf, 1024)) > 0) {
printf("%.*s", res, buf);
readcount++;
}
if(res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
perror("failed to receive message");
goto end_err;
}
if(readcount == 0) {
printf("peer terminated call\n");
goto end;
}
}
}
end:
close(sock);
exit(0);
end_err:
close(sock);
exit(1);
}
int connect_local_sock(char *path) {
int res;
int sock;
struct sockaddr_un addr;
memset(&addr, 0, sizeof(struct sockaddr_un));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if(sock < 0) {
perror("error opening local socket");
exit(1);
}
res = connect(sock, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un));
if(res < 0) {
perror("error connecting to local socket");
exit(1);
}
return sock;
}
void pickup(struct sockaddr *addr) {
int res;
int sock = connect_local_sock("/tmp/tel.sock");
res = tel_write_u32(sock, MSG_PICKUP);
if(res < 0) goto write_err;
res = tel_write_sockaddr(sock, addr);
if(res < 0) goto write_err;
uint32_t rsp;
res = tel_read_u32(sock, &rsp);
if(res < 0) goto read_err;
switch(rsp) {
case MSG_NONE:
printf("no incoming calls from specified address\n");
close(sock);
return;
case MSG_ERROR: {
uint32_t len;
res = tel_read_u32(sock, &len);
if(res < 0) goto read_err;
char buf[len + 1];
res = tel_read_all(sock, buf, len);
if(res < 0) goto read_err;
buf[len] = '\0';
fprintf(stderr, "error in daemon: %s\n", buf);
close(sock);
return;
}
case MSG_SOCKET: {
int fd;
int res = tel_read_fd(sock, &fd);
if(res < 0) {
perror("failed to receive fd");
return;
} else if(res == 0) {
fprintf(stderr, "failed to receive fd: no data\n");
return;
}
close(sock);
printf("picked up call\n");
handle_call(fd);
return;
}
default:
close(sock);
return;
}
read_err:
perror("error reading socket");
goto end;
write_err:
perror("error writing socket");
goto end;
end:
close(sock);
}
void call(struct sockaddr *addr) {
int res;
int sock = connect_local_sock("/tmp/tel.sock");
res = tel_write_u32(sock, MSG_CALL);
if(res < 0) goto write_err;
res = tel_write_sockaddr(sock, addr);
if(res < 0) goto write_err;
uint32_t rsp;
res = tel_read_u32(sock, &rsp);
if(res < 0) goto read_err;
switch(rsp) {
case MSG_NONE:
printf("none\n");
close(sock);
return;
case MSG_ERROR: {
uint32_t len;
res = tel_read_u32(sock, &len);
if(res < 0) goto read_err;
char buf[len + 1];
res = tel_read_all(sock, buf, len);
if(res < 0) goto read_err;
buf[len] = '\0';
fprintf(stderr, "error in daemon: %s\n", buf);
close(sock);
return;
}
case MSG_SOCKET: {
int fd;
int res = tel_read_fd(sock, &fd);
if(res < 0) {
perror("failed to receive fd");
return;
} else if(res == 0) {
fprintf(stderr, "failed to receive fd: no data\n");
return;
}
close(sock);
printf("calling...\n");
handle_call(fd);
return;
}
default:
close(sock);
return;
}
read_err:
perror("error reading socket");
goto end;
write_err:
perror("error writing socket");
goto end;
end:
close(sock);
}
void usage_exit(char *name) {
fprintf(stderr, "usage: %s call <addr> [ip] OR %s pkup <addr> [ip]", name, name);
exit(1);
}
int main(int argc, char **argv) {
if(argc < 1) exit(1);
if(argc < 2) usage_exit(argv[0]);
if(strcmp(argv[1], "call") == 0) {
if(argc < 3 || argc > 4) usage_exit(argv[0]);
struct sockaddr *addr;
socklen_t addr_len;
int res = tel_get_addr(argv[2], (argc < 4) ? NULL : argv[3], &addr, &addr_len);
if(res != 0) {
fprintf(stderr, "error: %s\n", gai_strerror(res));
exit(1);
}
call(addr);
} else if(strcmp(argv[1], "pkup") == 0) {
if(argc < 3 || argc > 4) usage_exit(argv[0]);
struct sockaddr *addr;
socklen_t addr_len;
int res = tel_get_addr(argv[2], (argc < 4) ? NULL : argv[3], &addr, &addr_len);
if(res != 0) {
fprintf(stderr, "error: %s\n", gai_strerror(res));
exit(1);
}
pickup(addr);
} else {
usage_exit(argv[0]);
}
}