cacaserver.c (libcaca-0.99.beta19) | : | cacaserver.c (libcaca-0.99.beta20.tar.bz2) | ||
---|---|---|---|---|
/* | /* | |||
* cacaserver Colour ASCII-Art library | * cacaserver Colour ASCII-Art library | |||
* Copyright (c) 2006 Jean-Yves Lamoureux <jylam@lnxscene.org> | * Copyright © 2006-2021 Sam Hocevar <sam@hocevar.net> | |||
* 2006-2014 Sam Hocevar <sam@hocevar.net> | * 2016 Denis Fondras <ledeuns at github> | |||
* All Rights Reserved | * 2006 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | ||||
* | * | |||
* This program is free software. It comes without any warranty, to | * This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | * the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What the Fuck You Want | * and/or modify it under the terms of the Do What the Fuck You Want | |||
* to Public License, Version 2, as published by Sam Hocevar. See | * to Public License, Version 2, as published by Sam Hocevar. See | |||
* http://www.wtfpl.net/ for more details. | * http://www.wtfpl.net/ for more details. | |||
*/ | */ | |||
#include "config.h" | #include "config.h" | |||
skipping to change at line 34 | skipping to change at line 35 | |||
# define USE_WINSOCK 1 | # define USE_WINSOCK 1 | |||
#endif | #endif | |||
#if defined(HAVE_NETINET_IN_H) | #if defined(HAVE_NETINET_IN_H) | |||
# include <netinet/in.h> | # include <netinet/in.h> | |||
#endif | #endif | |||
#if defined(HAVE_UNISTD_H) | #if defined(HAVE_UNISTD_H) | |||
# include <unistd.h> | # include <unistd.h> | |||
#endif | #endif | |||
#include <sys/types.h> | #include <sys/types.h> | |||
#include <sys/socket.h> | #include <sys/socket.h> | |||
#include <netdb.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | |||
#include <signal.h> | #include <signal.h> | |||
#include <errno.h> | #include <errno.h> | |||
#include <stdarg.h> | #include <stdarg.h> | |||
#ifndef USE_WINSOCK | #ifndef USE_WINSOCK | |||
# define USE_WINSOCK 0 | # define USE_WINSOCK 0 | |||
#endif | #endif | |||
#include "caca.h" | #include "caca.h" | |||
skipping to change at line 97 | skipping to change at line 99 | |||
struct client | struct client | |||
{ | { | |||
int fd; | int fd; | |||
int ready; | int ready; | |||
uint8_t inbuf[INBUFFER]; | uint8_t inbuf[INBUFFER]; | |||
int inbytes; | int inbytes; | |||
uint8_t outbuf[OUTBUFFER]; | uint8_t outbuf[OUTBUFFER]; | |||
int start, stop; | int start, stop; | |||
}; | }; | |||
#define MAXSOCKS 16 | ||||
struct sock { | ||||
int sockfd; | ||||
struct sockaddr_in my_addr; | ||||
}; | ||||
struct server | struct server | |||
{ | { | |||
unsigned int width, height; | unsigned int width, height; | |||
unsigned int port; | unsigned int port; | |||
int sockfd; | int sock_count; | |||
struct sockaddr_in my_addr; | struct sock socks[MAXSOCKS]; | |||
socklen_t sin_size; | ||||
/* Input buffer */ | /* Input buffer */ | |||
uint8_t *input; | uint8_t *input; | |||
unsigned int read; | unsigned int read; | |||
char prefix[sizeof(INIT_PREFIX)]; | char prefix[sizeof(INIT_PREFIX)]; | |||
caca_canvas_t *canvas; | caca_canvas_t *canvas; | |||
void *buffer; | void *buffer; | |||
size_t buflen; | size_t buflen; | |||
int client_count; | int client_count; | |||
struct client *clients; | struct client *clients; | |||
RETSIGTYPE (*sigpipe_handler)(int); | void (*sigpipe_handler)(int); | |||
}; | }; | |||
static void manage_connections(struct server *server); | void fprint_ip(FILE *stream, struct sockaddr *ai); | |||
static void manage_connections(struct server *server, int sockfd); | ||||
static int send_data(struct server *server, struct client *c); | static int send_data(struct server *server, struct client *c); | |||
ssize_t nonblock_write(int fd, void *buf, size_t len); | ssize_t nonblock_write(int fd, void *buf, size_t len); | |||
int main(void) | int main(void) | |||
{ | { | |||
int i, yes = 1, flags; | int i, yes = 1, flags, fd, error; | |||
struct server *server; | struct server *server; | |||
struct addrinfo ai_hints, *ai, *res; | ||||
char port_str[6]; | ||||
char *tmp; | char *tmp; | |||
#if USE_WINSOCK | #if USE_WINSOCK | |||
WORD winsockVersion; | WORD winsockVersion; | |||
WSADATA wsaData; | WSADATA wsaData; | |||
winsockVersion = MAKEWORD(1, 1); | winsockVersion = MAKEWORD(1, 1); | |||
WSAStartup(winsockVersion, &wsaData); | WSAStartup(winsockVersion, &wsaData); | |||
#endif | #endif | |||
server = malloc(sizeof(struct server)); | server = malloc(sizeof(struct server)); | |||
skipping to change at line 155 | skipping to change at line 166 | |||
server->port = 0xCACA; /* 51914 */ | server->port = 0xCACA; /* 51914 */ | |||
/* FIXME, handle >255 sizes */ | /* FIXME, handle >255 sizes */ | |||
memcpy(server->prefix, INIT_PREFIX, sizeof(INIT_PREFIX)); | memcpy(server->prefix, INIT_PREFIX, sizeof(INIT_PREFIX)); | |||
tmp = strstr(server->prefix, "____"); | tmp = strstr(server->prefix, "____"); | |||
tmp[0] = (uint8_t) (server->width & 0xff00) >> 8; | tmp[0] = (uint8_t) (server->width & 0xff00) >> 8; | |||
tmp[1] = (uint8_t) server->width & 0xff; | tmp[1] = (uint8_t) server->width & 0xff; | |||
tmp[2] = (uint8_t) (server->height & 0xff00) >> 8; | tmp[2] = (uint8_t) (server->height & 0xff00) >> 8; | |||
tmp[3] = (uint8_t) server->height & 0xff; | tmp[3] = (uint8_t) server->height & 0xff; | |||
if((server->sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) | memset(&ai_hints, 0, sizeof(ai_hints)); | |||
ai_hints.ai_family = AF_UNSPEC; | ||||
ai_hints.ai_socktype = SOCK_STREAM; | ||||
ai_hints.ai_flags = AI_PASSIVE; | ||||
memset(port_str, 0, sizeof(port_str)); | ||||
snprintf(port_str, 6, "%d", server->port); | ||||
error = getaddrinfo(NULL, port_str, &ai_hints, &ai); | ||||
if (error) | ||||
{ | { | |||
perror("socket"); | perror("getaddrinfo"); | |||
return -1; | return -1; | |||
} | } | |||
if(setsockopt(server->sockfd, SOL_SOCKET, | for (res = ai; res && server->sock_count < MAXSOCKS; res = res->ai_next) | |||
SO_REUSEADDR, &yes, sizeof(int)) == -1) | ||||
{ | { | |||
perror("setsockopt SO_REUSEADDR"); | if ((fd = socket(res->ai_addr->sa_family, SOCK_STREAM, 0)) == -1) | |||
return -1; | { | |||
} | perror("socket"); | |||
continue; | ||||
server->my_addr.sin_family = AF_INET; | } | |||
server-> my_addr.sin_port = htons(server->port); | if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) | |||
server->my_addr.sin_addr.s_addr = INADDR_ANY; | { | |||
memset(&(server->my_addr.sin_zero), '\0', 8); | perror("setsockopt: SO_REUSEADDR"); | |||
continue; | ||||
} | ||||
if (res->ai_addr->sa_family == AF_INET6) | ||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(int)) == | ||||
-1) | ||||
{ | ||||
perror("setsockopt: IPV6_V6ONLY"); | ||||
continue; | ||||
} | ||||
if (bind(fd, res->ai_addr, res->ai_addrlen) == -1) | ||||
{ | ||||
perror("bind"); | ||||
continue; | ||||
} | ||||
flags = fcntl(fd, F_GETFL, 0); | ||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK); | ||||
if(listen(fd, BACKLOG) == -1) | ||||
{ | ||||
perror("listen"); | ||||
continue; | ||||
} | ||||
if(bind(server->sockfd, (struct sockaddr *)&server->my_addr, | server->socks[server->sock_count].sockfd = fd; | |||
sizeof(struct sockaddr)) == -1) | server->sock_count++; | |||
{ | fprintf(stderr, "listening on "); | |||
perror("bind"); | fprint_ip(stderr, res->ai_addr); | |||
return -1; | fprintf(stderr, "\n"); | |||
} | } | |||
freeaddrinfo(ai); | ||||
/* Non blocking socket */ | if (server->sock_count == 0) | |||
flags = fcntl(server->sockfd, F_GETFL, 0); | ||||
fcntl(server->sockfd, F_SETFL, flags | O_NONBLOCK); | ||||
if(listen(server->sockfd, BACKLOG) == -1) | ||||
{ | { | |||
perror("listen"); | fprintf(stderr, "Not listening\n"); | |||
return -1; | return -1; | |||
} | } | |||
server->canvas = caca_create_canvas(0, 0); | server->canvas = caca_create_canvas(0, 0); | |||
server->buffer = NULL; | server->buffer = NULL; | |||
/* Ignore SIGPIPE */ | /* Ignore SIGPIPE */ | |||
server->sigpipe_handler = signal(SIGPIPE, SIG_IGN); | server->sigpipe_handler = signal(SIGPIPE, SIG_IGN); | |||
fprintf(stderr, "initialised network, listening on port %i\n", | fprintf(stderr, "initialised network, listening on port %i\n", | |||
server->port); | server->port); | |||
/* Main loop */ | /* Main loop */ | |||
for(;;) | for(;;) | |||
{ | { | |||
restart: | restart: | |||
/* Manage new connections as this function will be called sometimes | /* Manage new connections as this function will be called sometimes | |||
* more often than display */ | * more often than display */ | |||
manage_connections(server); | for (i = 0; i < server->sock_count; i++) | |||
manage_connections(server, server->socks[i].sockfd); | ||||
/* Read data from stdin */ | /* Read data from stdin */ | |||
if(server->read < 12) | if(server->read < 12) | |||
{ | { | |||
read(0, server->input + server->read, 12 - server->read); | read(0, server->input + server->read, 12 - server->read); | |||
server->read = 12; | server->read = 12; | |||
} | } | |||
while(caca_import_canvas_from_memory(server->canvas, server->input, | while(caca_import_canvas_from_memory(server->canvas, server->input, | |||
server->read, "caca") < 0) | server->read, "caca") < 0) | |||
skipping to change at line 291 | skipping to change at line 326 | |||
} | } | |||
if(server->buffer) | if(server->buffer) | |||
free(server->buffer); | free(server->buffer); | |||
caca_free_canvas(server->canvas); | caca_free_canvas(server->canvas); | |||
/* Restore SIGPIPE handler */ | /* Restore SIGPIPE handler */ | |||
signal(SIGPIPE, server->sigpipe_handler); | signal(SIGPIPE, server->sigpipe_handler); | |||
for (i = 0; i < server->sock_count; i++) | ||||
close(server->socks[i].sockfd); | ||||
free(server); | free(server); | |||
#if USE_WINSOCK | #if USE_WINSOCK | |||
WSACleanup(); | WSACleanup(); | |||
#endif | #endif | |||
return 0; | return 0; | |||
} | } | |||
/* | /* | |||
* XXX: The following functions are local | * XXX: The following functions are local | |||
*/ | */ | |||
static void manage_connections(struct server *server) | void fprint_ip(FILE *stream, struct sockaddr *ai) | |||
{ | ||||
char buffer[INET6_ADDRSTRLEN]; | ||||
socklen_t len = sizeof(struct sockaddr_in6); | ||||
if (ai->sa_family == AF_INET) | ||||
len = sizeof(struct sockaddr_in); | ||||
int err = getnameinfo(ai, len, buffer, sizeof(buffer), NULL, 0, NI_NUMERICHO | ||||
ST); | ||||
if (err != 0) | ||||
fprintf(stream, "n/a"); | ||||
else | ||||
fprintf(stream, "%s", buffer); | ||||
} | ||||
static void manage_connections(struct server *server, int sockfd) | ||||
{ | { | |||
int fd, flags; | int fd, flags; | |||
struct sockaddr_in remote_addr; | struct sockaddr_in6 remote_addr; | |||
socklen_t len = sizeof(struct sockaddr_in); | socklen_t len = sizeof(struct sockaddr_in6); | |||
fd = accept(server->sockfd, (struct sockaddr *)&remote_addr, &len); | fd = accept(sockfd, (struct sockaddr*)&remote_addr, &len); | |||
if(fd == -1) | if(fd == -1) | |||
return; | return; | |||
fprintf(stderr, "[%i] connected from %s\n", | fprintf(stderr, "[%i] connected from ", fd); | |||
fd, inet_ntoa(remote_addr.sin_addr)); | fprint_ip(stderr, (struct sockaddr*)&remote_addr); | |||
fprintf(stderr, "\n"); | ||||
/* Non blocking socket */ | /* Non blocking socket */ | |||
flags = fcntl(fd, F_SETFL, 0); | flags = fcntl(fd, F_SETFL, 0); | |||
fcntl(fd, F_SETFL, flags | O_NONBLOCK); | fcntl(fd, F_SETFL, flags | O_NONBLOCK); | |||
if(server->clients == NULL) | if(server->clients == NULL) | |||
{ | { | |||
server->clients = malloc(sizeof(struct client)); | server->clients = malloc(sizeof(struct client)); | |||
if(server->clients == NULL) | if(server->clients == NULL) | |||
{ | { | |||
End of changes. 22 change blocks. | ||||
41 lines changed or deleted | 97 lines changed or added |