1/* 2 * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. 3 * Distributed under the terms of the MIT License. 4 */ 5 6#include <errno.h> 7#include <stdio.h> 8#include <string.h> 9#include <unistd.h> 10#include <sys/socket.h> 11#include <sys/un.h> 12 13#include "external_commands.h" 14#include "fs_shell_command_unix.h" 15 16static int sClientConnection = -1; 17 18static int 19get_command_socket() 20{ 21 static int fd = -1; 22 static bool initialized = false; 23 if (!initialized) { 24 // get the listener socket 25 fd = socket(AF_UNIX, SOCK_STREAM, 0); 26 if (fd < 0) 27 return -1; 28 29 // bind it to the port 30 sockaddr_un addr; 31 unlink(kFSShellCommandSocketAddress); 32 addr.sun_family = AF_UNIX; 33 strcpy(addr.sun_path, kFSShellCommandSocketAddress); 34 int addrLen = addr.sun_path + strlen(addr.sun_path) + 1 - (char*)&addr; 35 if (bind(fd, (sockaddr*)&addr, addrLen) < 0) { 36 close(fd); 37 return -1; 38 } 39 40 // start listening 41 if (listen(fd, 1) < 0) { 42 close(fd); 43 return -1; 44 } 45 46 initialized = true; 47 } 48 49 return fd; 50} 51 52 53static int 54get_client_connection() 55{ 56 if (sClientConnection >= 0) 57 return sClientConnection; 58 59 // get the listener socket 60 int commandFD = get_command_socket(); 61 if (commandFD < 0) 62 return -1; 63 64 // accept a connection 65 do { 66 sockaddr_un addr; 67 socklen_t addrLen = sizeof(addr); 68 sClientConnection = accept(commandFD, (sockaddr*)&addr, &addrLen); 69 } while (sClientConnection < 0 && errno == EINTR); 70 71 return sClientConnection; 72} 73 74 75static void 76close_client_connection() 77{ 78 if (sClientConnection >= 0) { 79 close(sClientConnection); 80 sClientConnection = -1; 81 } 82} 83 84 85char * 86get_external_command(const char *prompt, char *input, int len) 87{ 88 do { 89 // get a connection 90 int connection = get_client_connection(); 91 if (connection < 0) 92 return NULL; 93 94 // read until we have a full command 95 external_command_message message; 96 int toRead = sizeof(message); 97 char *buffer = (char*)&message; 98 while (toRead > 0) { 99 int bytesRead = read(connection, buffer, toRead); 100 if (bytesRead < 0) { 101 if (errno == EINTR) { 102 continue; 103 } else { 104 fprintf(stderr, "Reading from connection failed: %s\n", strerror(errno)); 105 break; 106 } 107 } 108 109 // connection closed? 110 if (bytesRead == 0) 111 break; 112 113 buffer += bytesRead; 114 toRead -= bytesRead; 115 } 116 117 // connection may be broken: discard it 118 if (toRead > 0) { 119 close_client_connection(); 120 continue; 121 } 122 123 // get the len of the command 124 message.command[sizeof(message.command) - 1] = '\0'; 125 int commandLen = strlen(message.command) + 1; 126 if (commandLen <= 1) { 127 fprintf(stderr, "No command given.\n"); 128 continue; 129 } 130 if (commandLen > len) { 131 fprintf(stderr, "Command too long. Ignored.\n"); 132 continue; 133 } 134 135 // copy the command 136 memcpy(input, message.command, commandLen); 137 input[len - 1] = '\0'; // always NULL-terminate 138 return input; 139 140 } while (true); 141} 142 143 144void 145reply_to_external_command(int result) 146{ 147 if (sClientConnection < 0) 148 return; 149 150 // prepare the reply 151 external_command_reply reply; 152 reply.error = result; 153 154 // send the reply 155 int toWrite = sizeof(reply); 156 char *replyBuffer = (char*)&reply; 157 ssize_t bytesWritten; 158 do { 159 bytesWritten = write(sClientConnection, replyBuffer, toWrite); 160 if (bytesWritten > 0) { 161 replyBuffer += bytesWritten; 162 toWrite -= bytesWritten; 163 } 164 } while (toWrite > 0 && !(bytesWritten < 0 && errno != EINTR)); 165 166 // connection may be broken: discard it 167 if (bytesWritten < 0) 168 close_client_connection(); 169} 170 171 172void 173external_command_cleanup() 174{ 175 unlink(kFSShellCommandSocketAddress); 176} 177