Add defence code to handle socket read failure
[platform/core/context/context-service.git] / src / agent / AgentSocket.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <sys/socket.h>
20 #include <sys/un.h>
21 #include <systemd/sd-daemon.h>
22 #include <stdexcept>
23 #include <IAgentPlugin.h>
24 #include "PluginLoader.h"
25 #include "AgentSocket.h"
26
27 #define PATH_LENGTH             48
28 #define TERM_MSG_ID             0xFFFF
29
30 using namespace ctx;
31
32 struct CommandInfo {
33         PluginLoader* pluginLoader;
34         uint16_t id;
35         uint8_t length;
36         void* command;
37
38         CommandInfo(PluginLoader* loader, uint16_t i, uint8_t len, char* cmd) :
39                 pluginLoader(loader), id(i), length(len), command(NULL)
40         {
41                 command = malloc(length);
42
43                 if (!command)
44                         throw std::bad_alloc();
45
46                 memcpy(command, cmd, length);
47         }
48
49         ~CommandInfo()
50         {
51                 free(command);
52         }
53 };
54
55 static gboolean __send_command(gpointer data)
56 {
57         CommandInfo* info = static_cast<CommandInfo*>(data);
58
59         info->pluginLoader->send(info->id, info->length, info->command);
60
61         delete info;
62
63         return G_SOURCE_REMOVE;
64 }
65
66 static bool __set_close_on_exec(int fd)
67 {
68         if (::fcntl(fd, F_SETFL, FD_CLOEXEC) == -1)
69                 return false;
70
71         return true;
72 }
73
74 static int __get_socket_fd(const char* path)
75 {
76         int n = sd_listen_fds(0);
77         IF_FAIL_RETURN_TAG(n > 0, E_ACCESS, _E, "sd_listen_fds() failed");
78
79         for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; ++fd) {
80                 if (sd_is_socket_unix(fd, SOCK_STREAM, 1, path, 0) > 0) {
81                         __set_close_on_exec(fd);
82                         return fd;
83                 }
84         }
85
86         return E_ACCESS;
87 }
88
89 AgentSocket::AgentSocket(PluginLoader& loader) :
90         __sockFd(-1),
91         __listening(false),
92         __listeningThread(NULL),
93         __pluginLoader(loader)
94 {
95         if (!__init()) {
96                 __release();
97                 throw std::runtime_error("Socket connection failed");
98         }
99
100         _I("Listening...");
101 }
102
103 AgentSocket::~AgentSocket()
104 {
105         __release();
106 }
107
108 bool AgentSocket::__init()
109 {
110         char path[PATH_LENGTH];
111         g_snprintf(path, PATH_LENGTH, CTX_AGENT_SOCKET, static_cast<unsigned>(getuid()));
112
113         _D("Socket Path: %s", path);
114
115         __sockFd = __get_socket_fd(path);
116         IF_FAIL_RETURN_TAG(__sockFd > 0, false, _E, "Failed to get the socket fd");
117
118         __listening.store(true);
119         __listeningThread = g_thread_new(NULL, __listen, this);
120         IF_FAIL_RETURN_TAG(__listeningThread, false, _E, "Thread creation failed");
121
122         return true;
123 }
124
125 gpointer AgentSocket::__listen(gpointer data)
126 {
127         static_cast<AgentSocket*>(data)->__listen();
128         return NULL;
129 }
130
131 void AgentSocket::__listen()
132 {
133         int msgsock = 0;
134
135         while (__listening.load()) {
136                 msgsock = accept(__sockFd, 0, 0);
137
138                 if (msgsock < 0) {
139                         _D("accept() failed");
140                         continue;
141                 }
142
143                 if (!__readCommand(msgsock)) {
144                         close(msgsock);
145                         break;
146                 }
147
148                 close(msgsock);
149         }
150 }
151
152 bool AgentSocket::__readCommand(int msgsock)
153 {
154         uint16_t id = 0;
155         uint8_t length = 0;
156         char command[CTX_AGENT_COMMAND_LIMIT] = {0};
157
158         auto safeRead = [&msgsock](char* buf, size_t count)->bool {
159                 do {
160                         int rc = read(msgsock, buf, count);
161                         if (rc < 0) {
162                                 if (errno == EAGAIN) {
163                                         continue;
164                                 } else {
165                                         _E("Socket read error: %d", errno);
166                                         return false;
167                                 }
168                         }
169                         count -= rc;
170                         buf += rc;
171                 } while (count > 0);
172                 return true;
173         };
174
175         bool success;
176
177         success = safeRead(reinterpret_cast<char*>(&id), sizeof(id));
178         IF_FAIL_RETURN(success, false);
179
180         if (id == TERM_MSG_ID) {
181                 _D("Stop listening...");
182                 return false;
183         }
184
185         success = safeRead(reinterpret_cast<char*>(&length), sizeof(length));
186         IF_FAIL_RETURN(success, false);
187         IF_FAIL_RETURN_TAG(length > 0 && length <= CTX_AGENT_COMMAND_LIMIT, false, _E, "Invalid command");
188
189         success = safeRead(command, length);
190         IF_FAIL_RETURN(success, false);
191
192         CommandInfo* info = new CommandInfo(&__pluginLoader, id, length, command);
193         g_idle_add(__send_command, info);
194
195         return true;
196 }
197
198 void AgentSocket::__release()
199 {
200         if (!__listening.load())
201                 return;
202
203         __listening.store(false);
204         __terminate();
205
206         if (__listeningThread)
207                 g_thread_join(__listeningThread);
208
209         close(__sockFd);
210 }
211
212 bool AgentSocket::__terminate()
213 {
214         int sock = -1;
215         sockaddr_un addr;
216         char path[PATH_LENGTH];
217         const uint16_t termMsg = TERM_MSG_ID;
218
219         g_snprintf(path, PATH_LENGTH, CTX_AGENT_SOCKET, static_cast<unsigned>(getuid()));
220         IF_FAIL_RETURN_TAG(strlen(path) < sizeof(addr.sun_path), false, _E, "Invalid path");
221
222         sock = socket(AF_UNIX, SOCK_STREAM, 0);
223         IF_FAIL_RETURN_TAG(sock > 0, false, _E, "socket creation failed");
224
225         bzero(&addr, sizeof(addr));
226         addr.sun_family = AF_UNIX;
227         strncpy(addr.sun_path, path, sizeof(addr.sun_path));
228         addr.sun_path[sizeof(path)] = '\0';
229
230         if (connect(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
231                 close(sock);
232                 _E("Connection failed");
233                 return false;
234         }
235
236         if (write(sock, &termMsg, sizeof(termMsg)) < 0) {
237                 close(sock);
238                 _E("Sending failed");
239                 return false;
240         }
241
242         close(sock);
243         return true;
244 }