e8d45b9f8758832a94db979d74345259c71c1952
[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 <fcntl.h>
18 #include <sys/socket.h>
19 #include <sys/un.h>
20 #include <systemd/sd-daemon.h>
21 #include <stdexcept>
22 #include <IAgentPlugin.h>
23 #include "PluginLoader.h"
24 #include "AgentSocket.h"
25
26 #define PATH_LENGTH             48
27 #define TERM_MSG_ID             0xFFFF
28
29 using namespace ctx;
30
31 struct CommandInfo {
32         PluginLoader* pluginLoader;
33         uint16_t id;
34         uint8_t length;
35         void* command;
36
37         CommandInfo(PluginLoader* loader, uint16_t i, uint8_t len, char* cmd) :
38                 pluginLoader(loader), id(i), length(len), command(NULL)
39         {
40                 command = malloc(length);
41
42                 if (!command)
43                         throw std::bad_alloc();
44
45                 memcpy(command, cmd, length);
46         }
47
48         ~CommandInfo()
49         {
50                 free(command);
51         }
52 };
53
54 static gboolean __send_command(gpointer data)
55 {
56         CommandInfo* info = static_cast<CommandInfo*>(data);
57
58         info->pluginLoader->send(info->id, info->length, info->command);
59
60         delete info;
61
62         return G_SOURCE_REMOVE;
63 }
64
65 static bool __set_close_on_exec(int fd)
66 {
67         if (::fcntl(fd, F_SETFL, FD_CLOEXEC) == -1)
68                 return false;
69
70         return true;
71 }
72
73 static int __get_socket_fd(const char* path)
74 {
75         int n = sd_listen_fds(0);
76         IF_FAIL_RETURN_TAG(n > 0, E_ACCESS, _E, "sd_listen_fds() failed");
77
78         for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; ++fd) {
79                 if (sd_is_socket_unix(fd, SOCK_STREAM, 1, path, 0) > 0) {
80                         __set_close_on_exec(fd);
81                         return fd;
82                 }
83         }
84
85         return E_ACCESS;
86 }
87
88 AgentSocket::AgentSocket(PluginLoader& loader) :
89         __sockFd(-1),
90         __listening(false),
91         __listeningThread(NULL),
92         __pluginLoader(loader)
93 {
94         if (!__init()) {
95                 __release();
96                 throw std::runtime_error("Socket connection failed");
97         }
98
99         _I("Listening...");
100 }
101
102 AgentSocket::~AgentSocket()
103 {
104         __release();
105 }
106
107 bool AgentSocket::__init()
108 {
109         char path[PATH_LENGTH];
110         g_snprintf(path, PATH_LENGTH, CTX_AGENT_SOCKET, static_cast<unsigned>(getuid()));
111
112         _D("Socket Path: %s", path);
113
114         __sockFd = __get_socket_fd(path);
115         IF_FAIL_RETURN_TAG(__sockFd > 0, false, _E, "Failed to get the socket fd");
116
117         __listening.store(true);
118         __listeningThread = g_thread_new(NULL, __listen, this);
119         IF_FAIL_RETURN_TAG(__listeningThread, false, _E, "Thread creation failed");
120
121         return true;
122 }
123
124 gpointer AgentSocket::__listen(gpointer data)
125 {
126         static_cast<AgentSocket*>(data)->__listen();
127         return NULL;
128 }
129
130 void AgentSocket::__listen()
131 {
132         int msgsock = 0;
133
134         while (__listening.load()) {
135                 msgsock = accept(__sockFd, 0, 0);
136
137                 if (msgsock < 0) {
138                         _D("accept() failed");
139                         continue;
140                 }
141
142                 if (!__readCommand(msgsock)) {
143                         close(msgsock);
144                         break;
145                 }
146
147                 close(msgsock);
148         }
149 }
150
151 bool AgentSocket::__readCommand(int msgsock)
152 {
153         uint16_t id = 0;
154         uint8_t length = 0;
155         int rval = 0;
156         char buf[CTX_AGENT_COMMAND_LIMIT] = {0};
157
158         rval = read(msgsock, &id, sizeof(id));
159         IF_FAIL_RETURN_TAG(rval == sizeof(id), false, _E, "read() failed");
160
161         if (id == TERM_MSG_ID) {
162                 _D("Stop listening...");
163                 return false;
164         }
165
166         rval = read(msgsock, &length, sizeof(length));
167         IF_FAIL_RETURN_TAG(rval == sizeof(length), false, _E, "read() failed");
168
169         rval = read(msgsock, buf, length);
170         IF_FAIL_RETURN_TAG(rval == static_cast<int>(length), false, _E, "read() failed");
171
172
173         buf[length] = '\0';
174
175         CommandInfo* info = new CommandInfo(&__pluginLoader, id, length, buf);
176         g_idle_add(__send_command, info);
177
178         return true;
179 }
180
181 void AgentSocket::__release()
182 {
183         if (!__listening.load())
184                 return;
185
186         __listening.store(false);
187         __terminate();
188
189         if (__listeningThread)
190                 g_thread_join(__listeningThread);
191
192         close(__sockFd);
193 }
194
195 bool AgentSocket::__terminate()
196 {
197         int sock = -1;
198         sockaddr_un addr;
199         char path[PATH_LENGTH];
200         const uint16_t termMsg = TERM_MSG_ID;
201
202         g_snprintf(path, PATH_LENGTH, CTX_AGENT_SOCKET, static_cast<unsigned>(getuid()));
203         IF_FAIL_RETURN_TAG(strlen(path) < sizeof(addr.sun_path), false, _E, "Invalid path");
204
205         sock = socket(AF_UNIX, SOCK_STREAM, 0);
206         IF_FAIL_RETURN_TAG(sock > 0, false, _E, "socket creation failed");
207
208         bzero(&addr, sizeof(addr));
209         addr.sun_family = AF_UNIX;
210         strncpy(addr.sun_path, path, sizeof(addr.sun_path));
211         addr.sun_path[sizeof(path)] = '\0';
212
213         if (connect(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
214                 close(sock);
215                 _E("Connection failed");
216                 return false;
217         }
218
219         if (write(sock, &termMsg, sizeof(termMsg)) < 0) {
220                 close(sock);
221                 _E("Sending failed");
222                 return false;
223         }
224
225         close(sock);
226         return true;
227 }