1 /* 2 * Copyright 2005-2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. 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 16 17 static int sClientConnection = -1; 18 19 20 static int 21 get_command_socket() 22 { 23 static int fd = -1; 24 static bool initialized = false; 25 if (!initialized) { 26 // get the listener socket 27 fd = socket(AF_UNIX, SOCK_STREAM, 0); 28 if (fd < 0) 29 return -1; 30 31 // bind it to the port 32 sockaddr_un addr; 33 unlink(kFSShellCommandSocketAddress); 34 addr.sun_family = AF_UNIX; 35 strcpy(addr.sun_path, kFSShellCommandSocketAddress); 36 int addrLen = addr.sun_path + strlen(addr.sun_path) + 1 - (char*)&addr; 37 if (bind(fd, (sockaddr*)&addr, addrLen) < 0) { 38 close(fd); 39 return -1; 40 } 41 42 // start listening 43 if (listen(fd, 1) < 0) { 44 close(fd); 45 return -1; 46 } 47 48 initialized = true; 49 } 50 51 return fd; 52 } 53 54 55 static int 56 get_client_connection() 57 { 58 if (sClientConnection >= 0) 59 return sClientConnection; 60 61 // get the listener socket 62 int commandFD = get_command_socket(); 63 if (commandFD < 0) 64 return -1; 65 66 // accept a connection 67 do { 68 sockaddr_un addr; 69 socklen_t addrLen = sizeof(addr); 70 sClientConnection = accept(commandFD, (sockaddr*)&addr, &addrLen); 71 } while (sClientConnection < 0 && errno == EINTR); 72 73 return sClientConnection; 74 } 75 76 77 static void 78 close_client_connection() 79 { 80 if (sClientConnection >= 0) { 81 close(sClientConnection); 82 sClientConnection = -1; 83 } 84 } 85 86 87 static bool 88 read_data(int fd, void* _buffer, size_t toRead) 89 { 90 char* buffer = (char*)_buffer; 91 92 ssize_t bytesRead = 0; 93 while (toRead > 0 && !(bytesRead < 0 && errno != EINTR)) { 94 bytesRead = read(fd, buffer, toRead); 95 if (bytesRead == 0) 96 break; 97 if (bytesRead > 0) { 98 buffer += bytesRead; 99 toRead -= bytesRead; 100 } 101 } 102 103 return (toRead == 0); 104 } 105 106 107 bool 108 FSShell::get_external_command(char *input, int len) 109 { 110 do { 111 // get a connection 112 int connection = get_client_connection(); 113 if (connection < 0) 114 return false; 115 116 // read command message 117 external_command_message message; 118 if (!read_data(connection, &message, sizeof(message))) { 119 // that usually means the connection was closed 120 close_client_connection(); 121 continue; 122 } 123 124 // check command length 125 if (message.command_length >= (unsigned)len) { 126 fprintf(stderr, "Error: Command too long!\n"); 127 close_client_connection(); 128 continue; 129 } 130 131 // read the command 132 if (!read_data(connection, input, message.command_length)) { 133 fprintf(stderr, "Error: Reading from connection failed: " 134 "%s\n", strerror(errno)); 135 close_client_connection(); 136 continue; 137 } 138 139 // null-terminate 140 input[message.command_length] = '\0'; 141 142 return true; 143 144 } while (true); 145 } 146 147 148 void 149 FSShell::reply_to_external_command(int result) 150 { 151 if (sClientConnection < 0) 152 return; 153 154 // prepare the reply 155 external_command_reply reply; 156 reply.error = result; 157 158 // send the reply 159 int toWrite = sizeof(reply); 160 char *replyBuffer = (char*)&reply; 161 ssize_t bytesWritten; 162 do { 163 bytesWritten = write(sClientConnection, replyBuffer, toWrite); 164 if (bytesWritten > 0) { 165 replyBuffer += bytesWritten; 166 toWrite -= bytesWritten; 167 } 168 } while (toWrite > 0 && !(bytesWritten < 0 && errno != EINTR)); 169 170 // connection may be broken: discard it 171 if (bytesWritten < 0) 172 close_client_connection(); 173 } 174 175 176 void 177 FSShell::external_command_cleanup() 178 { 179 unlink(kFSShellCommandSocketAddress); 180 } 181