/* * Copyright © 2019 Advanced Driver Information Technology GmbH * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /******************************************************************************* ** ** ** TARGET : linux ** ** ** ** PROJECT : waltham-receiver ** ** ** ** PURPOSE : This file handles connection with remote-client ** ** ** *******************************************************************************/ #include #include #include #include #include #include "wth-receiver-comm.h" #define MAX_EPOLL_WATCHES 2 #define DEFAULT_TCP_PORT 34400 uint16_t tcp_port = 0; const char *my_app_id = NULL; static bool *signal_int_handler_run_flag; /** Print out the application help */ static void usage(void) { printf("Usage: waltham receiver [options]\n"); printf("Options:\n"); printf(" -p --port number TCP port number\n"); printf(" -i --app_id Specify an app_id\n"); printf(" -h --help Usage\n"); } static struct option long_options[] = { {"port", required_argument, 0, 'p'}, {"app_id", required_argument, NULL, 'i'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; /** * parse_args * * Parses the application arguments * The arguments are parsed and saved in static structure for future use. * * @param argc The amount of arguments * @param argv The table of arguments * * @return 0 on success, -1 otherwise */ static int parse_args(int argc, char *argv[]) { int c = -1; int long_index = 0; while ((c = getopt_long(argc, argv, "i:p:vh", long_options, &long_index)) != -1) { switch (c) { case 'i': my_app_id = optarg; break; case 'p': tcp_port = (uint16_t) atoi(optarg); break; case 'v': printf("No verbose logs for release mode"); break; case 'h': usage(); return -1; default: wth_error("Try %s -h for more information.\n", argv[0]); return -1; } } if (tcp_port == 0) { tcp_port = DEFAULT_TCP_PORT; } return 0; } static int watch_ctl(struct watch *w, int op, uint32_t events) { struct epoll_event ee; ee.events = events; ee.data.ptr = w; return epoll_ctl(w->receiver->epoll_fd, op, w->fd, &ee); } /** * listen_socket_handle_data * * Handles all incoming events on socket * * @param names struct watch *w ,uint32_t events * @param value pointer to watch connection it holds receiver information, Incoming events information * @return none */ static void listen_socket_handle_data(struct watch *w, uint32_t events) { struct receiver *srv = container_of(w, struct receiver, listen_watch); if (events & EPOLLERR) { wth_error("Listening socket errored out.\n"); srv->running = false; return; } if (events & EPOLLHUP) { wth_error("Listening socket hung up.\n"); srv->running = false; return; } if (events & EPOLLIN) { receiver_accept_client(srv); } } static void receiver_clean_up_child(pid_t pid, struct receiver *srv) { struct client *client; /* client represents connection with the transmitter, so removing it * from the client list isn't what need to happen. Mark the * pid_destroying as to give it a chance for next time */ wl_list_for_each(client, &srv->client_list, link) { if (client->pid == pid) { client->pid_destroying = false; } } } static void wait_for_children(struct receiver *srv, pid_t pid) { pid_t w; int status; int options = WUNTRACED | WCONTINUED; do { w = waitpid(pid, &status, options); if (w == -1) { fprintf(stderr, "err no children to wait for!\n"); } if (WIFEXITED(status)) { if (w > 0) { receiver_clean_up_child(w, srv); } } else if (WIFSIGNALED(status)) { // FIXME should remove the client here, and identify with the pid } else if (WIFSTOPPED(status)) { } else if (WIFCONTINUED(status)) { } } while (!WIFEXITED(status) && !WIFSIGNALED(status)); } /** * receiver_mainloop * * This is the main loop, which will flush all pending clients requests and * listen to input events from socket * * @param names void *data * @param value pointer to receiver struct - * struct holds the client connection information * @return none */ static void receiver_mainloop(struct receiver *srv) { struct epoll_event ee[MAX_EPOLL_WATCHES]; struct watch *w; int count; int i; srv->running = true; while (srv->running) { /* Run any idle tasks at this point. */ bool should_wait_for_children = false; struct client *client; receiver_flush_clients(srv); wl_list_for_each(client, &srv->client_list, link) { if (client->pid > 0 && client->pid_destroying) { should_wait_for_children = true; break; } } /* * wait_for_children() will block and wait for children that * died out/terminated correctly as the remote that end sent * the destroy surface request. pid_destroying is the one that * allows to make that distinction and will be set by the * destroy surface handler. * * 'children' is just one. A new child will be started when the * remote side sents a request to create a surface and will be * killed in the handler that destroys the surface. */ if (should_wait_for_children) wait_for_children(srv, client->pid); /* Wait for events or signals */ count = epoll_wait(srv->epoll_fd, ee, ARRAY_LENGTH(ee), -1); if (count < 0 && errno != EINTR) { perror("Error with epoll_wait"); break; } /* Handle all fds, both the listening socket * (see listen_socket_handle_data()) and clients * (see connection_handle_data()). */ for (i = 0; i < count; i++) { w = ee[i].data.ptr; w->cb(w, ee[i].events); } } } static int receiver_listen(uint16_t tcp_port) { int fd; int reuse = 1; struct sockaddr_in addr; fd = socket(AF_INET, SOCK_STREAM, 0); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(tcp_port); addr.sin_addr.s_addr = htonl(INADDR_ANY); setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse); if (bind(fd, (struct sockaddr *)&addr, sizeof addr) < 0) { wth_error("Failed to bind to port %d", tcp_port); close(fd); return -1; } if (listen(fd, 1024) < 0) { wth_error("Failed to listen to port %d", tcp_port); close (fd); return -1; } return fd; } static void signal_int_handler(int signum) { if (!*signal_int_handler_run_flag) abort(); *signal_int_handler_run_flag = false; } static void set_sigint_handler(bool *running) { struct sigaction sigint; signal_int_handler_run_flag = running; sigint.sa_handler = signal_int_handler; sigemptyset(&sigint.sa_mask); sigint.sa_flags = SA_RESETHAND; sigaction(SIGINT, &sigint, NULL); } /** * main * * waltham receiver main function, it accepts tcp port number as argument. * Establishes connection on the port and listen to port for incoming connection * request from waltham clients * * @param names argv - argument list and argc -argument count * @param value tcp port number as argument * @return 0 on success, -1 on error */ int main(int argc, char *argv[]) { struct receiver srv = { 0 }; struct client *c; /* Get command line arguments */ if (parse_args(argc, argv) != 0) { return -1; } set_sigint_handler(&srv.running); wl_list_init(&srv.client_list); srv.epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (srv.epoll_fd == -1) { perror("Error on epoll_create1"); exit(1); } srv.listen_fd = receiver_listen(tcp_port); if (srv.listen_fd < 0) { perror("Error setting up listening socket"); exit(1); } srv.listen_watch.receiver = &srv; srv.listen_watch.cb = listen_socket_handle_data; srv.listen_watch.fd = srv.listen_fd; if (watch_ctl(&srv.listen_watch, EPOLL_CTL_ADD, EPOLLIN) < 0) { perror("Error setting up listen polling"); exit(1); } receiver_mainloop(&srv); /* destroy all things */ wl_list_last_until_empty(c, &srv.client_list, link) client_destroy(c); close(srv.listen_fd); close(srv.epoll_fd); return 0; }