xref: /haiku/src/tools/fs_shell/external_commands_unix.cpp (revision 56eb8e78cc702792e3b032e3f5f45da9e5dbea9e)
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