Fix shutdown procedure
[connman] / plugins / supplicant.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <net/if.h>
35
36 #include <glib.h>
37
38 #include "supplicant.h"
39
40 struct supplicant_task {
41         GPid pid;
42         int ifindex;
43         char *ifname;
44         struct connman_iface *iface;
45         int socket;
46         GIOChannel *channel;
47 };
48
49 static GSList *tasks = NULL;
50
51 static struct supplicant_task *find_task(int ifindex)
52 {
53         GSList *list;
54
55         for (list = tasks; list; list = list->next) {
56                 struct supplicant_task *task = list->data;
57
58                 if (task->ifindex == ifindex) 
59                         return task;
60         }
61
62         return NULL;
63 }
64
65 static int exec_cmd(struct supplicant_task *task, char *cmd)
66 {
67         write(task->socket, cmd, strlen(cmd));
68
69         return 0;
70 }
71
72 static gboolean control_event(GIOChannel *chan,
73                                 GIOCondition cond, gpointer data)
74 {
75         struct supplicant_task *task = data;
76         char buf[256];
77         gsize len;
78         GIOError err;
79
80         if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
81                 g_io_channel_unref(chan);
82                 return FALSE;
83         }
84
85         memset(buf, 0, sizeof(buf));
86
87         err = g_io_channel_read(chan, buf, sizeof(buf), &len);
88         if (err) {
89                 if (err == G_IO_ERROR_AGAIN)
90                         return TRUE;
91                 g_io_channel_unref(chan);
92                 return FALSE;
93         }
94
95         if (buf[0] != '<')
96                 return TRUE;
97
98         printf("[SUPPLICANT] %s\n", buf + 3);
99
100         if (g_str_has_prefix(buf + 3, "CTRL-EVENT-CONNECTED") == TRUE) {
101                 printf("[SUPPLICANT] connected\n");
102                 connman_iface_update(task->iface,
103                                         CONNMAN_IFACE_STATE_CARRIER);
104         }
105
106         if (g_str_has_prefix(buf + 3, "CTRL-EVENT-DISCONNECTED") == TRUE) {
107                 printf("[SUPPLICANT] disconnected\n");
108         }
109
110         if (g_str_has_prefix(buf + 3, "CTRL-EVENT-TERMINATING") == TRUE) {
111                 printf("[SUPPLICANT] terminating\n");
112         }
113
114         return TRUE;
115 }
116
117 static int open_control(struct supplicant_task *task)
118 {
119         struct sockaddr_un addr;
120         int sk;
121
122         printf("[SUPPLICANT] open control for %s\n", task->ifname);
123
124         sk = socket(PF_UNIX, SOCK_DGRAM, 0);
125         if (sk < 0)
126                 return -1;
127
128         memset(&addr, 0, sizeof(addr));
129         addr.sun_family = AF_UNIX;
130         snprintf(addr.sun_path, sizeof(addr.sun_path),
131                                         "%s/%s.cli", STATEDIR, task->ifname);
132         //unlink(addr.sun_path);
133
134         if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
135                 close(sk);
136                 return -1;
137         }
138
139         memset(&addr, 0, sizeof(addr));
140         addr.sun_family = AF_UNIX;
141         snprintf(addr.sun_path, sizeof(addr.sun_path),
142                                         "%s/%s", STATEDIR, task->ifname);
143
144         if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
145                 close(sk);
146                 return -1;
147         }
148
149         task->socket = sk;
150
151         task->channel = g_io_channel_unix_new(sk);
152         g_io_channel_set_close_on_unref(task->channel, TRUE);
153
154         g_io_add_watch(task->channel,
155                         G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
156                                                 control_event, task);
157
158         exec_cmd(task, "ATTACH");
159         exec_cmd(task, "ADD_NETWORK");
160
161         g_io_channel_unref(task->channel);
162
163         return 0;
164 }
165
166 int __supplicant_start(struct connman_iface *iface)
167 {
168         struct ifreq ifr;
169         struct supplicant_task *task;
170         char *argv[9];
171         int sk, err;
172
173         sk = socket(PF_INET, SOCK_DGRAM, 0);
174         if (sk < 0)
175                 return -EIO;
176
177         memset(&ifr, 0, sizeof(ifr));
178         ifr.ifr_ifindex = iface->index;
179
180         err = ioctl(sk, SIOCGIFNAME, &ifr);
181
182         close(sk);
183
184         if (err < 0)
185                 return -EIO;
186
187         printf("[SUPPLICANT] start %s\n", ifr.ifr_name);
188
189         task = g_try_new0(struct supplicant_task, 1);
190         if (task == NULL)
191                 return -ENOMEM;
192
193         task->ifindex = iface->index;
194         task->ifname = strdup(ifr.ifr_name);
195         task->iface = iface;
196
197         if (task->ifname == NULL) {
198                 g_free(task);
199                 return -ENOMEM;
200         }
201
202         argv[0] = "/sbin/wpa_supplicant";
203         argv[1] = "-qq";
204         argv[2] = "-C";
205         argv[3] = STATEDIR;
206         argv[4] = "-D";
207         argv[5] = "wext";
208         argv[6] = "-i";
209         argv[7] = task->ifname;
210         argv[8] = NULL;
211
212         if (g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
213                                 NULL, NULL, &task->pid, NULL) == FALSE) {
214                 printf("Failed to spawn wpa_supplicant\n");
215                 return -1;
216         }
217
218         tasks = g_slist_append(tasks, task);
219
220         printf("[SUPPLICANT] executed with pid %d\n", task->pid);
221
222         sleep(1);
223
224         task->socket = -1;
225
226         if (open_control(task) < 0)
227                 printf("[SUPPLICANT] control failed\n");
228
229         return 0;
230 }
231
232 int __supplicant_stop(struct connman_iface *iface)
233 {
234         struct supplicant_task *task;
235         char pathname[PATH_MAX];
236
237         task = find_task(iface->index);
238         if (task == NULL)
239                 return -ENODEV;
240
241         printf("[SUPPLICANT] stop %s\n", task->ifname);
242
243         tasks = g_slist_remove(tasks, task);
244
245         exec_cmd(task, "DETACH");
246
247         sleep(1);
248
249         kill(task->pid, SIGTERM);
250
251         //close(task->socket);
252         g_io_channel_unref(task->channel);
253
254         snprintf(pathname, sizeof(pathname),
255                                         "%s/%s.cli", STATEDIR, task->ifname);
256         unlink(pathname);
257
258         free(task->ifname);
259
260         g_free(task);
261
262         return 0;
263 }
264
265 int __supplicant_connect(struct connman_iface *iface,
266                                 const char *network, const char *passphrase)
267 {
268         struct supplicant_task *task;
269         char cmd[128];
270
271         task = find_task(iface->index);
272         if (task == NULL)
273                 return -ENODEV;
274
275         printf("[SUPPLICANT] connect %s\n", task->ifname);
276
277         exec_cmd(task, "DISABLE_NETWORK 0");
278
279         sprintf(cmd, "SET_NETWORK 0 ssid \"%s\"", network);
280         exec_cmd(task, cmd);
281
282         if (passphrase && strlen(passphrase) > 0) {
283                 exec_cmd(task, "SET_NETWORK 0 proto RSN WPA");
284                 exec_cmd(task, "SET_NETWORK 0 key_mgmt WPA-PSK");
285
286                 sprintf(cmd, "SET_NETWORK 0 psk \"%s\"", passphrase);
287                 exec_cmd(task, cmd);
288         } else {
289                 exec_cmd(task, "SET_NETWORK 0 proto RSN WPA");
290                 exec_cmd(task, "SET_NETWORK 0 key_mgmt NONE");
291         }
292
293         exec_cmd(task, "ENABLE_NETWORK 0");
294
295         return 0;
296 }