#include #include #include #include #include #include #include "rs232.h" #include /* struct describing the various templates that alerts can use * these are loaded from /etc/squishy */ struct template { //Name of template char * name; /* The actual template, '#' represents characters that could be * overridden w/ the message this is normalized at read time so that * lines will always end with \n\r, otherwise the alert becomes * misaligned even on systems where lines don't typically end with \r */ char * template; /* *Whether or not the template contains color escape sequences. * 0 is none; 1 is 16 color; 2 is 256bit color */ unsigned int color; /* len of name and template, including trailing '\0' * */ unsigned int name_len; unsigned int template_len; }; /* * GLOBAL VARIABLES, collection of templates and the length of that array */ struct template * * templates; unsigned int templates_len = 0; /* * Comport number of the comport in use */ int comport_number; /* * Reads alert template from file, returns null on failure, otherwise returns * pointer to the template w/ all data already allocated by the function */ struct template * read_template(char * path) { /* file the template is stored in * The encoding scheme does not matter * \n, \n\r, or \r endings will be normalized to \n\r */ FILE * template_file; /* * Pointer to the template that should be returned */ struct template * tmp; template_file = fopen(path, "r"); if (template_file == NULL) { fprintf(stderr,"Error reading from template file \"%s\"\n",path); return NULL; } /* * Allocation of the template struct */ tmp = (struct template *)malloc(sizeof(struct template)); if (tmp == NULL) { fprintf(stderr,"Error allocating memory for template \"%s\" of \ size %d bytes\n", path, sizeof(struct template)); return NULL; } /* * Length of template name string and first character of that string in * the string 'path' */ int namelen = 0; int parsepos = 0; /* * Byte for storing each character of path as it is being scanned */ char pathchar; /* finds the length of the slice of 'path' containing the filename, * inclusive of the trailing '\0'. Since it always tries get atleast the * first character of 'path', we have to make sure 'path' is not null, * although if this were the case fopen() above should have returned * null before it reaches this point. */ if (path == NULL) { fprintf(stderr, "NULL path in reading of template\n"); return NULL; } do { pathchar = *(path +parsepos); if (pathchar == '/' ) { namelen = 0; } else { namelen++; } parsepos++; } while (pathchar != '\0'); /* as parselen is the position of '\0' + 1, we want it is the total * length of path including the trailing '\0' * Therefore to get the position of the first byte of a slice of length * 'namelen' including the \0 would be parsepos - namelen */ tmp->name = malloc(namelen * sizeof(char)); if (tmp->name == NULL ) { fprintf(stderr, "Error allocating memory for template name\n"); return NULL; } strncpy(tmp->name, path+ (parsepos - namelen), namelen * sizeof(char)); //Name length tmp->name_len = namelen; /* * The variable that each byte stored from the stream is stored in */ char readbyte; /* * allocates initial char, which will be the trailing '\0' */ tmp->template = malloc(1 * sizeof(char)); tmp->template[0] = '\0'; //size of template including trailign '\0' int t_size = 1; /* * Loop goes through every character, reallocates tmp->template to * t_size + 1, then set new t_size -2 to that character, while * also setting t_size - 1 to '\0', thereby preserving the * trailing '\0'. * as the EOF is checked after each iteration, EOF is guaranteed to be * a part of the string. */ do { readbyte = (char)fgetc(template_file); tmp->template = realloc(tmp->template, sizeof(char) *(t_size +1)); if (tmp->template == NULL) { return NULL; } t_size++; tmp->template[t_size - 2] = readbyte; tmp->template[t_size - 1] = '\0'; /* * If the byte is '\n' it will iterate more, since EOF is * checked afterwards anyways, This doesn't matter. */ if (readbyte == '\n') { readbyte = (char)fgetc(template_file); if (readbyte != '\r') { tmp->template = realloc(tmp->template, sizeof(char) * (t_size + 1)); if (tmp->template == NULL) { return NULL; } t_size++; tmp->template[t_size - 2] = readbyte; tmp->template[t_size - 1] = '\0'; } else { tmp->template = realloc(tmp->template, sizeof(char) * (t_size + 2)); if (tmp->template == NULL) { return NULL; } t_size = t_size + 2; tmp->template[t_size - 3] = '\r'; tmp->template[t_size - 2] = readbyte; tmp->template[t_size - 1] = '\0'; } } } while (readbyte != (char)EOF); /* * Removes EOF before '\0' */ tmp->template = realloc(tmp->template, t_size - 1); if (tmp->template == NULL) { return NULL; } t_size--; tmp->template[t_size -1 ] = '\0'; //Template Length tmp->template_len = t_size; /* * Closes template_file */ fclose(template_file); return tmp; } /* * Prints out an alert message to pts device file output, using message-template * as the template and message as the message * beep causes it to play the bell-char at the end if it is != 0 * beep is not a bool because C80/C90 compatibility is important */ int alert (FILE * output, struct template * message_template, char * message, int beep) { /* alert is the string that will eventually be printed * assuming template_len will never be 0 */ char * alert = malloc(message_template->template_len * sizeof(char)); if (alert == NULL) { fprintf(stderr, "Error Memory Allocation in alert()\n"); return -1; } /* * Copies ->template into alert */ strncpy(alert, message_template->template, message_template->template_len * sizeof(char)); //index of bytes in template unsigned int i = 0; //index of bytes in message unsigned int m_i = 0; //boolean for determining whether 'message' has reached it's end short m_end = 0; /* loop through alert and try to fill '#' with either message or space * This excludes the trailing '\0' */ while (i < (message_template->template_len-1)) { if (alert[i] == '#') { if (!m_end) { /* * Handles breaking text by inserting a "-"/' ' * If this next character is not a #, then there is a * break. If the next cahracter in 'message' is * [A-Z][a-z] then we insert the break * This will apply even if message[m_i] is at * the end of the string as message[m_i+1] would * be the trailing '\0' * The same applies to alert[i+1]; */ if ((alert[i+1] != '#') && (isalpha(message[m_i+1])) && isalpha(message[m_i]) ) { /* * If the current message[m_i] and the * previous messae[m_i-1] are alpha * then we use dashes, otherwise it is * replaced with a ' ' */ if (message[m_i] == 0) { alert[i] = ' '; } else if(isalpha(message[m_i-1])) { alert[i] = '-'; } else { alert[i] = ' '; } } else { alert[i] = message[m_i]; m_i++; if (message[m_i] == '\0') { m_end = 1; } } } else { alert[i] = ' '; } } i++; } /* actually prints the alert string * \033 = * 7 saves cursor and attributes * 8 restores * [0;0H homes the cursor * [?47h * [?47l */ fprintf(output,"\0337\033[0;0H%s\0338",alert); /* the below loads an alternative buffer, this is deprecated*/ //fprintf(output,"\033[?47h%s\033[?47l",alert); if (beep) { fprintf(output, "\a"); } return 0; } /* * Concatenates two strings and returns the result as a heap allocated string * Returns null on failure */ char * str_concat(char * s1, char * s2) { char * return_str; /* * since strlen does not count the trailing '\0', we need to add one */ return_str = malloc((strlen(s1) + 1 + strlen(s2)) * sizeof(char) ); if (return_str == NULL) { fprintf(stderr,"Failure to allocate memory in str_concat()\n"); return NULL; } /* * copies the nonnull bytes of s1 */ strncpy(return_str,s1,sizeof(char) * strlen(s1)); /* * Copies the nonnull bytes of s2, which begin at strlen(s1) since that * is the offset from the beginning of s1 where the null byte of s1 * would be */ strncpy((return_str + strlen(s1)),s2,sizeof(char) * strlen(s2)); /* * Add trailing '\0' */ strncpy(return_str + strlen(s1) + strlen(s2) ,"\0", sizeof(char) * 1); return return_str; } /* * Loads all templates * looks in templates/ * returns NULL on error */ struct template * * load_templates() { /*ensures that templates is alteast 1 element big*/ struct template * * templates = (struct template * *)malloc(sizeof(struct template * )); /*opens templates dir*/ DIR * templates_dir = opendir("/etc/guardian/templates"); if (templates_dir == NULL) { return NULL; } /* * Structs storing the current loaded template and current directory * entry */ struct template * tem; struct dirent * entry; /* the loop keeps reading until entry is nyll, since the evaluation is * at the end, a switch has to ensure that no attempts to handle entry * == NULL are made */ do { entry = readdir(templates_dir); if (entry != NULL && entry->d_name[0] != '.') { /* since d_name is just the name of the file and we want * the full path */ char * fullpath = str_concat("/etc/guardian/templates/", entry->d_name); if (fullpath == NULL) { return NULL; } /* * Load the template */ tem = read_template(fullpath); //free heap allocated string ASAP free(fullpath); if ( tem == NULL ) { return NULL; } /*resizing templates*/ templates_len++; templates = (struct template * *)realloc(templates, templates_len * sizeof(struct template * )); if (templates == NULL) { return NULL; } templates[templates_len - 1] = tem; } } while (entry != NULL); /* * Close the templates directory */ closedir(templates_dir); return templates; } /* * Sends alert to all pseudoterminals the process has permission to * returns -1 on error * template_name is used to select a template and not a pointer to a template * class, this way broadcast handles template selection directly from arguments */ int broadcast(char * template_name, char * message) { struct template * tm = NULL; /* * loops through for the appropriate template */ for (int i = 0; i < templates_len; i++) { if (strcmp(templates[i]->name,template_name) == 0 ) { tm = templates[i]; } } /* * if none are found then tm is still null and can not proceed */ if (tm == NULL) { fprintf(stderr, "unable to find template %s",template_name); return -1; } /* * Directory pts contains all pseudo-terminal devices. * All terminals will have an alert sent to them. */ DIR * pts_dir = opendir("/dev/pts"); if (pts_dir == NULL ) { return -1; } /* * for storing the current dirent and pseudotty file */ struct dirent * pts_entry; FILE * pts; do { pts_entry = readdir(pts_dir); /* * Makes sure it's not readin from the ptmx device, a hidden * file, or NULL */ if ( pts_entry != NULL && pts_entry->d_name[0] != '.' && strcmp(pts_entry->d_name, "ptmx") != 0) { /* * Gets full path */ char * fullpath = str_concat("/dev/pts/", pts_entry->d_name); /* * opens file in append mode, these are the only * permissions we need */ pts = fopen(fullpath, "a"); if (pts == NULL) { fprintf(stderr, "unable to open file %s\n", fullpath); return -1; } /*frees heap allocated string*/ free(fullpath); alert(pts,tm,message,1); /*closes file*/ fclose(pts); } } while (pts_entry != NULL); } /* * Initializes RS232 connection with the guardian, returns -1 on error */ int startcom (char * device_path) { comport_number = -1; /* * Looks for device in the RS232 library array comports that match the * device specified by the user. If one is not found then comport_number * will remain -1; */ for (int i = 0; i < 38; i++) { if (strcmp(comports[i], device_path) == 0) { comport_number = i; } } /* * Deals with the condition described above */ if (comport_number == -1 ) { fprintf(stderr, "Could not find device %s\n",device_path); return -1; } /* * Opens RS232 Connection w/ the Guardian. The connection is at * 9600baud. 8N1 means 8 data bits and no parity bits */ int err = RS232_OpenComport(comport_number ,9600,"8N1"); if (err != 0) { fprintf(stderr, "Error opening comport %s, number %d\n", device_path, comport_number); return -1; } /* waits for arduino to respond, this indicates that it is ready to * accept commands. We know for a fact that the response is less than * 256 bytes so the buffer is more than safe */ char poll_buffer[256]; /* * Timeout is implemented with a timer w/ no regard to actual time * passed. For now it will be 200 tries */ int timeout = 0; do { /*On EAGAIN( no data), the function returns 0, however if there * is an error it returns <0. Otherwise it returns the number of * bytes read */ err = RS232_PollComport(comport_number, poll_buffer, 256); timeout++; /* * Checks for timeout */ if (timeout >= 200) { fprintf(stderr, "Polling %s timed out\n",device_path); err = -1; } } while (err == 0); if (err < 0) { fprintf(stderr, "Error polling from comport %s, number %d\n", device_path, comport_number); return -1; } return 0; } /* * Makes the Guardian block the keyboard, returns -1 on error */ int slap(char pos) { /* positions commands are sent in the form of * p[int] where the letter 'p' is followed by an integer ranging 0 to * 180 that determines the angle of the servo. We will cast this as an * char for simplicity. On the microcontroller side this gets casted * into an int 180 flat to the extreme * ------ * =====O | 180 Degrees * ------ * * | * __|___ * | O | 90 Degrees * ------ */ char command[2] = {'p', pos}; int error = RS232_SendBuf(comport_number,command, 2); if (error != 0) { fprintf(stderr, "Error sending command to comport %d\n", comport_number); return error; } return 0; } int main (int argc, char * argv[]) { /* * Checks for the proper number of arguments, also checks for argv[1] == * "raise" and there are 2 arguments. */ if (argc < 4) { /* * Tests for raise */ if (argc >= 3) { if (strcmp(argv[1], "raise") == 0 ) { /* * Starts com based on 2nd argument device and * raises the arm */ startcom(argv[2]); slap(90); /* exits */ return 0; } } /* * Not enough arguments, display help and exit */ fprintf(stdout, "Usage: guardian [template] [message] [guardian\ device]\n\ OR guardian raise"); return -1; } /* * Loads templates for the alert */ templates = load_templates(); if (templates == NULL) { return -1; } /* * Sends the alert to all pttys */ if (broadcast(argv[1],argv[2])) { return -1; } /* * Starts serial communications w/ the Arduino, uses 3rd argument as the * device to connect to. */ startcom(argv[3]); /* * For normal operation it lowers the arm */ slap(180); }