/* * Copyright (c) 2019 Clementine Computing LLC. * * This file is part of PopuFare. * * PopuFare is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PopuFare 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with PopuFare. If not, see . * */ #include #include #include #include #include #include #include #include #include #include "../common/common_defs.h" #include "commhub.h" //This function gets the number of microseconds since epoch for storing in message headers and //comparison to determine how timely a message is. long long int get_usec_time() { struct timeval tv; //Get the number of seconds (and remainder microseconds) since epoch. gettimeofday(&tv,NULL); //Return those in a single sane 64 bit integer containing the total number //of microseconds since epoch by multiplying tv_sec by a million and summing //the two values return (long long int)tv.tv_usec + (1000000 * (long long int)tv.tv_sec); } //This function does a non-blocking poll to determine if the message passing socket is //ready for input or output (depending on //since it is much more efficient to block on the entire list of your I/O file descriptors //and service any that cause you to unblock. // // Returns: MSG_NO_ACTION = no message // MSG_ERROR = fatal error (passed fd is no good) // // If the socket is ready, the return value will be // whatever combination of events specified in mask (MSG_SEND, MSG_RECV) // that is actually ready. // int message_socket_status(int message_socket_fd, int mask) { struct pollfd fds[1]; int retval; fds[0].fd = message_socket_fd; do { fds[0].events = mask; retval = poll(fds,1,0); //poll the 1 file descriptor, returning immediately. if(retval < 0) { //if we were interrupted by a signal, try again... if(errno == EINTR) continue; //otherwise poll() has failed, which is BAD news. return MSG_ERROR; } if(retval == 0) { return MSG_NO_ACTION; } //If poll tells us that the fd is invalid or has encountered a serious error, return -1. if(fds[0].revents & (POLLERR | POLLNVAL)) { return MSG_ERROR; } //If any of the events supplied in mask have occurred, report them. if(fds[0].revents & mask) { return fds[0].revents & mask; } //if there are no more message to drain (see above) and a hangup has occurred, //let the user know the socket is done for. if(fds[0].revents & POLLHUP) { return MSG_ERROR; } //Otherwise, by process of elimination we have no message and no error condition to report return MSG_NO_ACTION; } while(1); } //this function will pack a mailbox address and a payload into the supplied message structure and //set the correct timestamp. int prepare_message(struct message_record *target, char *mailbox, void *payload, unsigned int payload_len) { if(target == NULL) return -1; if(mailbox == NULL) return -1; if(payload == NULL) return -1; if(payload_len > MAX_PAYLOAD_LENGTH) return -1; memset(target, 0, sizeof(struct message_record)); strncpy(target->header.mailbox_name, mailbox, MAILBOX_NAME_MAX); target->header.mailbox_name[MAILBOX_NAME_MAX] = '\0'; target->header.usec_time = get_usec_time(); target->header.payload_length = payload_len; target->header.sender = getpid(); target->header.from_fd = 0; memmove(target->payload, payload, payload_len); return 0; } //the send_message function will transmit a message and its header (trimmed to the specified payload length). //zero will be returned on success, -1 on failure. int send_message(int message_socket_fd, struct message_record *message) { int txlen; int retval; if(message == NULL) return -1; //If we are trying to send a message that is longer than allowed, throw an error! if(message->header.payload_length > MAX_PAYLOAD_LENGTH) return -1; //Calculate the total message length (header + payload) so that we don't send //any extra padding that we don't need. txlen = sizeof(struct message_header_record) + message->header.payload_length; do { retval = send(message_socket_fd, message, txlen, MSG_EOR); //if we were interrupted by a signal, try again. if( (retval == -1) && (errno == EINTR) ) { continue; } //if we tried to send a message and it got truncated, return failure. if(retval != txlen) { return -1; } return 0; } while(1); } //the get_message function will receive a message from the opposite end of the socket and place it in the //supplied message structure incliding its header. -1 is returned on failure, 0 on success. int get_message(int message_socket_fd, struct message_record *message) { int retval; if(message == NULL) return -1; //clear our buffer of any garbage before using it to read in the message memset(message, 0, sizeof(struct message_record)); do { //try and receive our message retval = recv(message_socket_fd, message, sizeof(struct message_record), 0); //if we were interrupted by a signal, try again. if( (retval == -1) && (errno == EINTR) ) { continue; } //zero is a special case for recv which indicates that the other side has shut down the connection. if(retval == 0) { return -1; } //if the received message size does not add up (between header and payload) if(retval != (sizeof(struct message_header_record) + message->header.payload_length)) { return -1; //throw an error! } //Record which IPC socket we got this from (for things like PING replies) message->header.from_fd = message_socket_fd; return 0; //otherwise, return success } while(1); } //This function connects to the communication hub and returns the file descriptor of the connection. //If the connection failed, -1 is returned. The progname parameter (if not NULL) will be used to //register a module name (snagged from argv[0]) with the server for ease of debugging. int connect_to_message_server(char *progname) { int fd; int retval; int len; char *message_text; struct sockaddr_un addr; struct message_record msg; fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); if(fd < 0) return -1; //construct our address structure (pointing to the agreed upon server address) //to pass to connect. addr.sun_family = AF_UNIX; strncpy(addr.sun_path, COMMHUB_ADDRESS, sizeof(addr.sun_path) - 1); addr.sun_path[sizeof(addr.sun_path) - 1]='\0'; len=strlen(COMMHUB_ADDRESS) + sizeof(addr.sun_family); //attempt to connect to said server retval = connect(fd,(struct sockaddr *)&addr,len); if(retval) { fprintf(stderr, "Cannot connect to IPC hub at %s\n", COMMHUB_ADDRESS); close(fd); return -1; } //if we have been passed a module name to register as, use that, otherwise default to "ANONYMOUS" if(progname) { message_text = progname; } else { message_text = "ANONYMOUS"; } len = strlen(message_text); //if for some inconceivable reason our module name is longer than the server allows, truncate it. if(len > MAX_MODULE_NAME_LENGTH) { len = MAX_MODULE_NAME_LENGTH; } //prepare and send our registation message prepare_message(&msg, MAILBOX_HELLO, message_text, len); send_message(fd, &msg); return fd; } //---------------------------------------------------------------------