summaryrefslogtreecommitdiff
path: root/src/bin/efl
diff options
context:
space:
mode:
authorDaniel Zaoui <daniel.zaoui@yahoo.com>2016-11-27 07:48:10 +0200
committerDaniel Zaoui <daniel.zaoui@yahoo.com>2017-06-05 08:51:49 +0300
commit5f268ec26a76b130a78ab182981dde372951f075 (patch)
tree4aa3adb9ffa7fddcb2d87b5d16798381456a595f /src/bin/efl
parente53b77d6ce9848bdb274b428c33e6848c8620443 (diff)
First patch of the Eina Debug layer rewriting
Eina Debug is a new layer aimed for EFL debugging. It offers scalability by allowing registration of operations not specific to EFL core. The protocol is simple and the APIs try to provide as much functionalities/freedom as possible.
Diffstat (limited to 'src/bin/efl')
-rw-r--r--src/bin/efl/efl_debug.c397
-rw-r--r--src/bin/efl/efl_debug_common.c87
-rw-r--r--src/bin/efl/efl_debug_common.h55
-rw-r--r--src/bin/efl/efl_debugd.c811
4 files changed, 645 insertions, 705 deletions
diff --git a/src/bin/efl/efl_debug.c b/src/bin/efl/efl_debug.c
index 28cdb39d24..7876a08c18 100644
--- a/src/bin/efl/efl_debug.c
+++ b/src/bin/efl/efl_debug.c
@@ -16,269 +16,250 @@
16 * if not, see <http://www.gnu.org/licenses/>. 16 * if not, see <http://www.gnu.org/licenses/>.
17 */ 17 */
18 18
19#define DECLARE_OPS 19#include <Eina.h>
20#include "efl_debug_common.h" 20#include <Ecore.h>
21 21
22static Eo *dialer; 22# ifdef HAVE_CONFIG_H
23# include "config.h"
24# endif
23 25
24static Eina_List *waiting; 26#define EXTRACT(_buf, pval, sz) \
27{ \
28 memcpy(pval, _buf, sz); \
29 _buf += sz; \
30}
31#define _EVLOG_INTERVAL 0.2
25 32
26static int retval = EXIT_SUCCESS; 33static int _evlog_max_times = 0;
34static Ecore_Timer *_evlog_fetch_timer = NULL;
35static FILE *_evlog_file = NULL;
27 36
28static void 37static int _cl_stat_reg_opcode = EINA_DEBUG_OPCODE_INVALID;
29_process_reply(void *data EINA_UNUSED, const char op[static 4], const Eina_Slice payload) 38static int _cid_from_pid_opcode = EINA_DEBUG_OPCODE_INVALID;
30{ 39static int _prof_on_opcode = EINA_DEBUG_OPCODE_INVALID;
31 if (IS_OP(CLST)) 40static int _prof_off_opcode = EINA_DEBUG_OPCODE_INVALID;
32 { 41static int _cpufreq_on_opcode = EINA_DEBUG_OPCODE_INVALID;
33 int mypid = getpid(); 42static int _cpufreq_off_opcode = EINA_DEBUG_OPCODE_INVALID;
34 size_t offset; 43static int _evlog_get_opcode = EINA_DEBUG_OPCODE_INVALID;
35 44
36 waiting = eina_list_remove(waiting, OP_CLST); 45static Eina_Debug_Session *_session = NULL;
37 46
38 for (offset = 0; offset + sizeof(int) <= payload.len; offset += sizeof(int)) 47static int _cid = 0;
39 {
40 int p;
41 48
42 memcpy(&p, payload.bytes + offset, sizeof(int)); 49static int my_argc = 0;
50static char **my_argv = NULL;
43 51
44 if (p == mypid) continue; 52static Eina_Debug_Error
45 if (p > 0) printf("%i\n", p); 53_evlog_get_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
54{
55 static int received_times = 0;
56 unsigned char *d = buffer;
57 unsigned int *overflow = (unsigned int *)(d + 0);
58 unsigned char *p = d + 4;
59 unsigned int blocksize = size - 4;
60
61 if(++received_times <= _evlog_max_times)
62 {
63 if ((_evlog_file) && (blocksize > 0))
64 {
65 unsigned int header[3];
66
67 header[0] = 0xffee211;
68 header[1] = blocksize;
69 header[2] = *overflow;
70 if (fwrite(header, 1, 12, _evlog_file) < 12 ||
71 fwrite(p, 1, blocksize, _evlog_file) < blocksize)
72 printf("Error writing bytes to evlog file\n");
46 } 73 }
47 } 74 }
48 else 75
76 if(received_times == _evlog_max_times)
49 { 77 {
50 fprintf(stderr, "ERROR: unexpected server reply: %.4s\n", op); 78 printf("Received last evlog response\n");
51 retval = EXIT_FAILURE; 79 if (_evlog_file) fclose(_evlog_file);
80 _evlog_file = NULL;
81 ecore_main_loop_quit();
52 } 82 }
53 83
54 if (!waiting) ecore_main_loop_quit(); 84 return EINA_DEBUG_OK;
55} 85}
56 86
57static void 87static Eina_Bool
58_on_data(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED) 88_cb_evlog(void *data EINA_UNUSED)
59{ 89{
60 if (!received_data(dialer, _process_reply, NULL)) 90 static int sent_times = 0;
91 Eina_Bool ret = ECORE_CALLBACK_RENEW;
92 if(++sent_times <= _evlog_max_times)
93 eina_debug_session_send(_session, _cid, _evlog_get_opcode, NULL, 0);
94
95 if(sent_times == _evlog_max_times)
61 { 96 {
62 retval = EXIT_FAILURE; 97 eina_debug_session_send(_session, _cid, _cpufreq_off_opcode, NULL, 0);
63 ecore_main_loop_quit(); 98 ecore_timer_del(_evlog_fetch_timer);
99 _evlog_fetch_timer = NULL;
100 ret = ECORE_CALLBACK_CANCEL;
64 } 101 }
102
103 return ret;
65} 104}
66 105
67static Eina_Bool 106static Eina_Debug_Error
68_command_send(const char op[static 4], const void *data, unsigned int len) 107_cid_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer, int size EINA_UNUSED)
69{ 108{
70 if (!send_data(dialer, op, data, len)) 109 _cid = *(int *)buffer;
110
111 const char *op_str = my_argv[1];
112 Eina_Bool quit = EINA_TRUE;
113
114 if ((!strcmp(op_str, "pon")) && (3 <= (my_argc - 1)))
115 {
116 int freq = atoi(my_argv[3]);
117 eina_debug_session_send(_session, _cid, _prof_on_opcode, &freq, sizeof(int));
118 }
119 else if (!strcmp(op_str, "poff"))
120 eina_debug_session_send(_session, _cid, _prof_off_opcode, NULL, 0);
121 else if (!strcmp(op_str, "evlogon") && (3 <= (my_argc - 1)))
71 { 122 {
72 retval = EXIT_FAILURE; 123 double max_time;
73 return EINA_FALSE; 124 sscanf(my_argv[3], "%lf", &max_time);
125 _evlog_max_times = max_time > 0 ? (max_time/_EVLOG_INTERVAL+1) : 1;
126 eina_debug_session_send(_session, 0, _cl_stat_reg_opcode, NULL, 0);
127 printf("Evlog request will be sent %d times\n", _evlog_max_times);
128 eina_debug_session_send(_session, _cid, _cpufreq_on_opcode, NULL, 0);
129
130 /* Creating the evlog file and setting the timer */
131 char path[4096];
132 int pid = atoi(my_argv[2]);
133 snprintf(path, sizeof(path), "%s/efl_debug_evlog-%ld.log",
134 getenv("HOME"), (long)pid);
135 _evlog_file = fopen(path, "wb");
136 _evlog_fetch_timer = ecore_timer_add(_EVLOG_INTERVAL, _cb_evlog, NULL);
137
138 quit = EINA_FALSE;
74 } 139 }
140 else if (!strcmp(op_str, "evlogoff"))
141 eina_debug_session_send(_session, _cid, _cpufreq_off_opcode, NULL, 0);
75 142
76 return EINA_TRUE; 143 if(quit)
77} 144 ecore_main_loop_quit();
78 145
79#define command_send(op, data, len) _command_send(OP_ ## op, data, len) 146 return EINA_DEBUG_OK;
147}
80 148
81static void 149static Eina_Debug_Error
82_write_finished(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED) 150_clients_info_added_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
83{ 151{
84 if (!waiting) ecore_main_loop_quit(); 152 char *buf = buffer;
153 while(size)
154 {
155 int cid, pid, len;
156 EXTRACT(buf, &cid, sizeof(int));
157 EXTRACT(buf, &pid, sizeof(int));
158 /* We dont need client notifications on evlog */
159 if(!_evlog_fetch_timer)
160 printf("Added: CID: %d - PID: %d - Name: %s\n", cid, pid, buf);
161 len = strlen(buf) + 1;
162 buf += len;
163 size -= (2 * sizeof(int) + len);
164 }
165 return EINA_DEBUG_OK;
85} 166}
86 167
87static void 168static Eina_Debug_Error
88_finished(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED) 169_clients_info_deleted_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
89{ 170{
90 ecore_main_loop_quit(); 171 char *buf = buffer;
172 while(size)
173 {
174 int cid;
175 EXTRACT(buf, &cid, sizeof(int));
176 size -= sizeof(int);
177
178 /* If client deleted dont send anymore evlog requests */
179 if(_evlog_fetch_timer)
180 {
181 if(_cid == cid)
182 {
183 printf("Evlog debugged App closed (CID: %d), stopping evlog\n", cid);
184 ecore_timer_del(_evlog_fetch_timer);
185 _evlog_fetch_timer = NULL;
186 fclose(_evlog_file);
187 _evlog_file = NULL;
188 ecore_main_loop_quit();
189 }
190 }
191 else
192 printf("Deleted: CID: %d\n", cid);
193 }
194 return EINA_DEBUG_OK;
91} 195}
92 196
93static void 197static void
94_error(void *data EINA_UNUSED, const Efl_Event *event) 198_ecore_thread_dispatcher(void *data)
95{ 199{
96 Eina_Error *perr = event->info; 200 eina_debug_dispatch(_session, data);
201}
97 202
98 fprintf(stderr, "ERROR: error communicating to %s: %s\n", 203Eina_Debug_Error
99 efl_net_dialer_address_dial_get(dialer), 204_disp_cb(Eina_Debug_Session *session EINA_UNUSED, void *buffer)
100 eina_error_msg_get(*perr)); 205{
101 retval = EXIT_FAILURE; 206 ecore_main_loop_thread_safe_call_async(_ecore_thread_dispatcher, buffer);
102 ecore_main_loop_quit(); 207 return EINA_DEBUG_OK;
103} 208}
104 209
105int 210static void
106main(int argc, char **argv) 211_args_handle(Eina_Bool flag)
107{ 212{
108 Eo *loop; 213 if (!flag) exit(0);
109 char *path; 214 eina_debug_session_dispatch_override(_session, _disp_cb);;
110 Eina_Error err;
111 int i;
112 215
113 if (argc < 2) 216 const char *op_str = my_argv[1];
217 if (op_str && !strcmp(op_str, "list"))
114 { 218 {
115 fprintf(stderr, "ERROR: missing argument.\n"); 219 eina_debug_session_send(_session, 0, _cl_stat_reg_opcode, NULL, 0);
116 return EXIT_FAILURE;
117 } 220 }
118 for (i = 1; i < argc; i++) 221 else if (2 <= my_argc - 1)
119 { 222 {
120 if ((strcmp(argv[i], "-h") != 0) && 223 int pid = atoi(my_argv[2]);
121 (strcmp(argv[i], "--help") != 0)) 224 eina_debug_session_send(_session, 0, _cid_from_pid_opcode, &pid, sizeof(int));
122 continue;
123
124 printf("Usage:\n"
125 "\n"
126 "\t%s <command> [arguments]\n"
127 "\n"
128 "where <command> is one of:\n"
129 "\tlist list connected process (pid)\n"
130 "\tpon <pid> <freq> enable profiling for <pid> at frequency <freq> in microseconds.\n"
131 "\tpoff <pid> disable profiling for <pid>\n"
132 "\tevlogon <pid> start logging events to ~/efl_debug_evlog-<pid>.log\n"
133 "\tevlogoff <pid> stop logging events from <pid>\n",
134 argv[0]);
135
136 return EXIT_SUCCESS;
137 } 225 }
226}
138 227
139 ecore_app_no_system_modules(); 228static const Eina_Debug_Opcode ops[] =
229{
230 {"daemon/observer/client/register", &_cl_stat_reg_opcode, NULL},
231 {"daemon/observer/slave_added", NULL, &_clients_info_added_cb},
232 {"daemon/observer/slave_deleted", NULL, &_clients_info_deleted_cb},
233 {"daemon/info/cid_from_pid", &_cid_from_pid_opcode, &_cid_get_cb},
234 {"profiler/on", &_prof_on_opcode, NULL},
235 {"profiler/off", &_prof_off_opcode, NULL},
236 {"cpufreq/on", &_cpufreq_on_opcode, NULL},
237 {"cpufreq/off", &_cpufreq_off_opcode, NULL},
238 {"evlog/get", &_evlog_get_opcode, _evlog_get_cb},
239 {NULL, NULL, NULL}
240};
140 241
242int
243main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
244{
141 eina_init(); 245 eina_init();
142 ecore_init(); 246 ecore_init();
143 ecore_con_init();
144
145 path = ecore_con_local_path_new(EINA_FALSE, "efl_debug", 0);
146 if (!path)
147 {
148 fprintf(stderr, "ERROR: could not get local communication path\n");
149 retval = EXIT_FAILURE;
150 goto end;
151 }
152 247
153 loop = ecore_main_loop_get(); 248 my_argc = argc;
154 249 my_argv = argv;
155#ifdef EFL_NET_DIALER_UNIX_CLASS
156 dialer = efl_add(EFL_NET_DIALER_SIMPLE_CLASS, loop,
157 efl_net_dialer_simple_inner_class_set(efl_added, EFL_NET_DIALER_UNIX_CLASS));
158#elif defined(EFL_NET_DIALER_WINDOWS_CLASS)
159 dialer = efl_add(EFL_NET_DIALER_SIMPLE_CLASS, loop,
160 efl_net_dialer_simple_inner_class_set(efl_added, EFL_NET_DIALER_WINDOWS_CLASS));
161#else
162 /* TODO: maybe start a TCP using locahost:12345?
163 * Right now eina_debug_monitor is only for AF_UNIX, so not an issue.
164 */
165 fprintf(stderr, "ERROR: your platform doesn't support Efl.Net.Dialer.*\n");
166#endif
167 if (!dialer)
168 {
169 fprintf(stderr, "ERROR: could not create communication dialer\n");
170 retval = EXIT_FAILURE;
171 goto end;
172 }
173 efl_event_callback_add(dialer, EFL_IO_BUFFERED_STREAM_EVENT_ERROR, _error, NULL);
174 efl_event_callback_add(dialer, EFL_IO_BUFFERED_STREAM_EVENT_SLICE_CHANGED, _on_data, NULL);
175 efl_event_callback_add(dialer, EFL_IO_BUFFERED_STREAM_EVENT_WRITE_FINISHED, _write_finished, NULL);
176 efl_event_callback_add(dialer, EFL_IO_BUFFERED_STREAM_EVENT_FINISHED, _finished, NULL);
177
178 for (i = 1; i < argc; i++)
179 {
180 const char *cmd = argv[i];
181
182 if (strcmp(cmd, "list") == 0)
183 {
184 if (!command_send(LIST, NULL, 0))
185 goto end;
186 waiting = eina_list_append(waiting, OP_CLST);
187 }
188 else if (strcmp(cmd, "pon") == 0)
189 {
190 if (i + 2 >= argc)
191 {
192 fprintf(stderr, "ERROR: missing argument: pon <pid> <freq>\n");
193 retval = EXIT_FAILURE;
194 goto end;
195 }
196 else
197 {
198 int data[2] = {atoi(argv[i + 1]), atoi(argv[1 + 2])};
199 if (!command_send(PLON, data, sizeof(data)))
200 goto end;
201 i += 2;
202 }
203 }
204 else if (strcmp(cmd, "poff") == 0)
205 {
206 if (i + 1 >= argc)
207 {
208 fprintf(stderr, "ERROR: missing argument: poff <pid>\n");
209 retval = EXIT_FAILURE;
210 goto end;
211 }
212 else
213 {
214 int data[1] = {atoi(argv[i + 1])};
215 if (!command_send(PLOF, data, sizeof(data)))
216 goto end;
217 i++;
218 }
219 }
220 else if (strcmp(cmd, "evlogon") == 0)
221 {
222 if (i + 1 >= argc)
223 {
224 fprintf(stderr, "ERROR: missing argument: evlogon <pid>\n");
225 retval = EXIT_FAILURE;
226 goto end;
227 }
228 else
229 {
230 int data[1] = {atoi(argv[i + 1])};
231 if (!command_send(EVON, data, sizeof(data)))
232 goto end;
233 i++;
234 }
235 }
236 else if (strcmp(cmd, "evlogoff") == 0)
237 {
238 if (i + 1 >= argc)
239 {
240 fprintf(stderr, "ERROR: missing argument: evlogoff <pid>\n");
241 retval = EXIT_FAILURE;
242 goto end;
243 }
244 else
245 {
246 int data[1] = {atoi(argv[i + 1])};
247 if (!command_send(EVOF, data, sizeof(data)))
248 goto end;
249 i++;
250 }
251 }
252 else
253 {
254 fprintf(stderr, "ERROR: unknown command: %s\n", argv[i]);
255 retval = EXIT_FAILURE;
256 goto end;
257 }
258 }
259 efl_io_buffered_stream_eos_mark(dialer);
260 250
261 err = efl_net_dialer_dial(dialer, path); 251 _session = eina_debug_local_connect(EINA_TRUE);
262 if (err) 252 if (!_session)
263 { 253 {
264 fprintf(stderr, "ERROR: could not connect '%s': %s\n", path, eina_error_msg_get(err)); 254 fprintf(stderr, "ERROR: Cannot connect to debug daemon.\n");
265 retval = EXIT_FAILURE; 255 return -1;
266 goto end;
267 } 256 }
257 eina_debug_opcodes_register(_session, ops, _args_handle);
268 258
269 ecore_main_loop_begin(); 259 ecore_main_loop_begin();
270 260
271 end:
272 eina_list_free(waiting);
273 efl_del(dialer);
274 free(path);
275
276 ecore_con_shutdown();
277 ecore_shutdown(); 261 ecore_shutdown();
278 eina_shutdown(); 262 eina_shutdown();
279 263
280 (void) OP_HELO; 264 return 0;
281 (void) OP_EVLG;
282
283 return retval;
284} 265}
diff --git a/src/bin/efl/efl_debug_common.c b/src/bin/efl/efl_debug_common.c
deleted file mode 100644
index f37f17f16b..0000000000
--- a/src/bin/efl/efl_debug_common.c
+++ /dev/null
@@ -1,87 +0,0 @@
1/* EINA - EFL data type library
2 * Copyright (C) 2015 Carsten Haitzler
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "efl_debug_common.h"
20
21Eina_Bool
22received_data(Eo *sock, void (*handle)(void *data, const char op[static 4], const Eina_Slice payload), const void *data)
23{
24 Eina_Slice slice, payload;
25 Efl_Debug_Message_Header msgheader;
26
27 slice = efl_io_buffered_stream_slice_get(sock);
28 if (slice.len < sizeof(msgheader))
29 return EINA_TRUE;
30
31 memcpy(&msgheader, slice.mem, sizeof(msgheader));
32 if (msgheader.size < 4) /* must contain at last 4 byte opcode */
33 {
34 fprintf(stderr, "ERROR: invalid message header, size=%u\n", msgheader.size);
35 return EINA_FALSE;
36 }
37
38 if (msgheader.size + 4 > slice.len)
39 return EINA_TRUE;
40
41 payload.bytes = slice.bytes + sizeof(msgheader);
42 payload.len = msgheader.size - 4;
43
44 handle((void *)data, msgheader.op, payload);
45
46 efl_io_buffered_stream_discard(sock, sizeof(msgheader) + payload.len);
47 return EINA_TRUE;
48}
49
50Eina_Bool
51send_data(Eo *sock, const char op[static 4], const void *data, unsigned int len)
52{
53 Eina_Error err;
54 Efl_Debug_Message_Header msghdr = {
55 .size = 4 + len,
56 };
57 Eina_Slice s, r;
58
59 memcpy(msghdr.op, op, 4);
60
61 s.mem = &msghdr;
62 s.len = sizeof(msghdr);
63
64 err = efl_io_writer_write(sock, &s, &r);
65 if (err || r.len) goto end;
66
67 if (!len) goto end;
68
69 s.mem = data;
70 s.len = len;
71 err = efl_io_writer_write(sock, &s, &r);
72
73 end:
74 if (err)
75 {
76 fprintf(stderr, "ERROR: could not queue message '%.4s': %s\n", op, eina_error_msg_get(err));
77 return EINA_FALSE;
78 }
79
80 if (r.len)
81 {
82 fprintf(stderr, "ERROR: could not queue message '%.4s': out of memory\n", op);
83 return EINA_FALSE;
84 }
85
86 return EINA_TRUE;
87}
diff --git a/src/bin/efl/efl_debug_common.h b/src/bin/efl/efl_debug_common.h
deleted file mode 100644
index 6a0ba0b416..0000000000
--- a/src/bin/efl/efl_debug_common.h
+++ /dev/null
@@ -1,55 +0,0 @@
1/* EINA - EFL data type library
2 * Copyright (C) 2015 Carsten Haitzler
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef EFL_DEBUG_COMMON_H
20#define EFL_DEBUG_COMMON_H 1
21
22#define EFL_BETA_API_SUPPORT 1
23#define EFL_EO_API_SUPPORT 1
24
25#include <Eina.h>
26#include <Ecore.h>
27#include <Ecore_Con.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include <string.h>
32
33typedef struct _Efl_Debug_Message_Header {
34 unsigned int size;
35 char op[4];
36} Efl_Debug_Message_Header;
37
38#define IS_OP(x) memcmp(op, OP_ ## x, 4) == 0
39
40#define DECLARE_OP(x) static char OP_ ## x[4] = #x
41#ifdef DECLARE_OPS
42DECLARE_OP(LIST);
43DECLARE_OP(CLST);
44DECLARE_OP(PLON);
45DECLARE_OP(PLOF);
46DECLARE_OP(EVON);
47DECLARE_OP(EVOF);
48DECLARE_OP(EVLG);
49DECLARE_OP(HELO);
50#endif
51
52Eina_Bool send_data(Eo *sock, const char op[static 4], const void *data, unsigned int len);
53Eina_Bool received_data(Eo *sock, void (*handle)(void *data, const char op[static 4], const Eina_Slice payload), const void *data);
54
55#endif
diff --git a/src/bin/efl/efl_debugd.c b/src/bin/efl/efl_debugd.c
index dfe175ce51..9da2648723 100644
--- a/src/bin/efl/efl_debugd.c
+++ b/src/bin/efl/efl_debugd.c
@@ -16,470 +16,571 @@
16 * if not, see <http://www.gnu.org/licenses/>. 16 * if not, see <http://www.gnu.org/licenses/>.
17 */ 17 */
18 18
19#define DECLARE_OPS 19#include <unistd.h>
20#include "efl_debug_common.h" 20#include <sys/epoll.h>
21#include <sys/types.h>
22#include <sys/socket.h>
23#include <sys/un.h>
24#include <fcntl.h>
25
26#include <Eina.h>
27#include <Ecore.h>
28
29#define STORE(_buf, pval, sz) \
30{ \
31 memcpy(_buf, pval, sz); \
32 _buf += sz; \
33}
34
35#define EXTRACT(_buf, pval, sz) \
36{ \
37 memcpy(pval, _buf, sz); \
38 _buf += sz; \
39}
21 40
22typedef struct _Client Client; 41typedef struct _Client Client;
23 42
24struct _Client 43struct _Client
25{ 44{
26 Eo *client; 45 Eina_Stringshare *app_name;
27
28 unsigned char *buf;
29 unsigned int buf_size;
30 46
31 Ecore_Timer *evlog_fetch_timer; 47 Ecore_Timer *evlog_fetch_timer;
32 int evlog_on; 48 int evlog_on;
33 FILE *evlog_file; 49 FILE *evlog_file;
34 50
35 int version; 51 int version;
52 int fd;
53 int cid;
36 pid_t pid; 54 pid_t pid;
55
56 Eina_Bool cl_stat_obs : 1;
57 Eina_Bool is_master : 1;
37}; 58};
38 59
39static Eo *server; 60static Eina_List *_clients = NULL;
40 61
41static Eina_List *clients = NULL; 62typedef Eina_Bool (*Opcode_Cb)(Client *client, void *buffer, int size);
42 63
43static int retval; 64static Eina_Hash *_string_to_opcode_hash = NULL;
44 65
45static int _log_dom = -1; 66static int _free_cid = 1;
46 67
47#ifdef ERR 68static int _clients_stat_register_opcode = EINA_DEBUG_OPCODE_INVALID;
48# undef ERR 69static int _slave_added_opcode = EINA_DEBUG_OPCODE_INVALID;
49#endif 70static int _slave_deleted_opcode = EINA_DEBUG_OPCODE_INVALID;
50#define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__) 71static int _cid_from_pid_opcode = EINA_DEBUG_OPCODE_INVALID;
72static int _test_loop_opcode = EINA_DEBUG_OPCODE_INVALID;
51 73
52#ifdef DBG 74typedef struct
53# undef DBG 75{
54#endif 76 int opcode;
55#define DBG(...) EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__) 77 Eina_Stringshare *opcode_string;
56 78 Opcode_Cb cb;
57#ifdef INF 79} Opcode_Information;
58# undef INF
59#endif
60#define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
61 80
62#ifdef WRN 81#define MAX_OPCODES 1000
63# undef WRN 82Opcode_Information *_opcodes[MAX_OPCODES];
64#endif
65#define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
66 83
67#ifdef CRI 84/* epoll stuff */
68# undef CRI 85#ifndef _WIN32
86static int _epfd = -1, _listening_master_fd = -1, _listening_slave_fd = -1;
69#endif 87#endif
70#define CRI(...) EINA_LOG_DOM_CRIT(_log_dom, __VA_ARGS__)
71
72
73#define send_cli(cl, op, data, len) \
74 do \
75 { \
76 if (!send_data(cl->client, OP_ ## op, data, len)) \
77 { \
78 if (!efl_io_closer_closed_get(cl->client)) \
79 efl_io_closer_close(cl->client); \
80 } \
81 } \
82 while (0)
83 88
84static Client * 89static Client *
85_client_pid_find(int pid) 90_client_find_by_cid(int cid)
86{ 91{
87 Client *c; 92 Client *c;
88 Eina_List *l; 93 Eina_List *l;
94 EINA_LIST_FOREACH(_clients, l, c)
95 if (c->cid == cid) return c;
96 return NULL;
97}
89 98
90 if (pid <= 0) return NULL; 99static Client *
91 EINA_LIST_FOREACH(clients, l, c) 100_client_find_by_pid(int pid)
92 { 101{
93 if (c->pid == pid) return c; 102 Client *c;
94 } 103 Eina_List *l;
104 EINA_LIST_FOREACH(_clients, l, c)
105 if (c->pid == pid) return c;
106 return NULL;
107}
95 108
96 WRN("no client pid=%d", pid); 109static Client *
110_client_find_by_fd(int fd)
111{
112 Eina_List *itr;
113 Client *c;
114 EINA_LIST_FOREACH(_clients, itr, c)
115 if (c->fd == fd) return c;
97 return NULL; 116 return NULL;
98} 117}
99 118
100static Eina_Bool 119static int
101_cb_evlog(void *data) 120_send(Client *dest, int opcode, void *payload, int payload_size)
102{ 121{
103 Client *c = data; 122 int size = sizeof(Eina_Debug_Packet_Header) + payload_size;
104 send_cli(c, EVLG, NULL, 0); 123 char *buf = alloca(size);
105 return EINA_TRUE; 124 Eina_Debug_Packet_Header *hdr = (Eina_Debug_Packet_Header *)buf;
125 hdr->size = size;
126 hdr->cid = 0;
127 hdr->thread_id = 0;
128 hdr->opcode = opcode;
129 memcpy(buf + sizeof(Eina_Debug_Packet_Header), payload, payload_size);
130 //printf("%d bytes sent (opcode %s) to %s fd %d\n", size, _opcodes[opcode]->opcode_string, dest->app_name, dest->fd);
131 return send(dest->fd, buf, size, 0);
106} 132}
107 133
108static void 134static void
109_process_command(void *data, const char op[static 4], const Eina_Slice payload) 135_client_del(Client *c)
110{ 136{
111 Client *c = data;
112 Client *c2; 137 Client *c2;
113 Eina_List *l; 138 if (!c) return;
114 139 Eina_List *itr;
115 DBG("client %p (%p) [pid:%d] op=%.4s payload=%zd", c, c->client, c->pid, op, payload.len);
116 140
117 if (IS_OP(HELO)) 141 _clients = eina_list_remove(_clients, c);
142 if (c->evlog_fetch_timer)
118 { 143 {
119 if (payload.len < sizeof(int) * 2) 144 ecore_timer_del(c->evlog_fetch_timer);
120 { 145 c->evlog_fetch_timer = NULL;
121 fprintf(stderr, "INFO: client %p [pid: %d] sent invalid HELO\n", c, (int)c->pid);
122 if (!efl_io_closer_closed_get(c->client))
123 efl_io_closer_close(c->client);
124 }
125 else
126 {
127 memcpy(&c->version, payload.bytes, sizeof(int));
128 memcpy(&c->pid, payload.bytes + sizeof(int), sizeof(int));
129 INF("client %p (%p) HELO version=%d, pid=%d", c, c->client, c->version, c->pid);
130 }
131 } 146 }
132 else if (IS_OP(LIST)) 147 if (c->evlog_file)
133 { 148 {
134 int n = eina_list_count(clients); 149 fclose(c->evlog_file);
135 unsigned int *pids = malloc(n * sizeof(int)); 150 c->evlog_file = NULL;
136 if (pids)
137 {
138 int i = 0;
139
140 EINA_LIST_FOREACH(clients, l, c2)
141 {
142 if (c2->pid == 0) continue; /* no HELO yet */
143 pids[i] = c2->pid;
144 i++;
145 }
146 send_cli(c, CLST, pids, i * sizeof(int));
147 free(pids);
148 }
149 } 151 }
150 else if (IS_OP(PLON)) 152
153 /* Don't update the observers if the client is a master */
154 if (c->is_master) return;
155
156 EINA_LIST_FOREACH(_clients, itr, c2)
151 { 157 {
152 if (payload.len < sizeof(int) * 2) 158 if (c2->cl_stat_obs) _send(c2, _slave_deleted_opcode, &c->cid, sizeof(int));
153 fprintf(stderr, "INFO: client %p [pid: %d] sent invalid PLON\n", c, (int)c->pid);
154 else
155 {
156 int pid;
157 unsigned int freq;
158 memcpy(&pid, payload.bytes, sizeof(int));
159 memcpy(&freq, payload.bytes + sizeof(int), sizeof(int));
160 c2 = _client_pid_find(pid);
161 if (!c2)
162 {
163 fprintf(stderr, "INFO: client %p [pid: %d] sent PLON %d: no such client\n", c, (int)c->pid, pid);
164 }
165 else
166 {
167 DBG("client %p (%p) [pid:%d] requested PLON on %p (%p) [pid:%d]",
168 c, c->client, c->pid,
169 c2, c2->client, c2->pid);
170 send_cli(c2, PLON, &freq, sizeof(freq));
171 }
172 }
173 } 159 }
174 else if (IS_OP(PLOF)) 160 free(c);
161}
162
163static Eina_Bool
164_dispatch(Client *src, void *buffer, int size)
165{
166 Eina_Debug_Packet_Header *hdr = (Eina_Debug_Packet_Header *)buffer;
167 if (hdr->cid)
175 { 168 {
176 if (payload.len < sizeof(int)) 169 /* If the client id is given, we forward */
177 fprintf(stderr, "INFO: client %p [pid: %d] sent invalid PLOF\n", c, (int)c->pid); 170 Client *dest = _client_find_by_cid(hdr->cid);
178 else 171 if (dest)
179 { 172 {
180 int pid; 173 if (dest->is_master != src->is_master)
181 memcpy(&pid, payload.bytes, sizeof(int));
182 c2 = _client_pid_find(pid);
183 if (!c2)
184 { 174 {
185 fprintf(stderr, "INFO: client %p [pid: %d] sent PLOF %d: no such client\n", c, (int)c->pid, pid); 175 hdr->cid = src->cid;
176 send(dest->fd, buffer, size, 0);
186 } 177 }
187 else 178 else
188 { 179 {
189 DBG("client %p (%p) [pid:%d] requested PLOF on %p (%p) [pid:%d]", 180 /*
190 c, c->client, c->pid, 181 * Packets Master -> Master or Slave -> Slave are forbidden
191 c2, c2->client, c2->pid); 182 * Only Master <-> Slave packets are allowed.
192 send_cli(c2, PLOF, NULL, 0); 183 */
184 printf("Packet from %d to %d: denied (same type)\n", hdr->cid, dest->cid);
193 } 185 }
194 } 186 }
195 } 187 }
196 else if (IS_OP(EVON)) 188 else
197 { 189 {
198 if (payload.len < sizeof(int)) 190 printf("Invoke %s\n", _opcodes[hdr->opcode]->opcode_string);
199 fprintf(stderr, "INFO: client %p [pid: %d] sent invalid EVON\n", c, (int)c->pid); 191 if (_opcodes[hdr->opcode]->cb)
200 else 192 return _opcodes[hdr->opcode]->cb(src,
201 { 193 (char *)buffer + sizeof(Eina_Debug_Packet_Header), size - sizeof(Eina_Debug_Packet_Header));
202 int pid;
203 memcpy(&pid, payload.bytes, sizeof(int));
204 c2 = _client_pid_find(pid);
205 if (!c2)
206 {
207 fprintf(stderr, "INFO: client %p [pid: %d] sent EVON %d: no such client\n", c, (int)c->pid, pid);
208 }
209 else
210 {
211 c2->evlog_on++;
212 DBG("client %p (%p) [pid:%d] requested EVON (%d) on %p (%p) [pid:%d]",
213 c, c->client, c->pid,
214 c2->evlog_on,
215 c2, c2->client, c2->pid);
216 if (c2->evlog_on == 1)
217 {
218 char buf[4096];
219
220 send_cli(c2, EVON, NULL, 0);
221 c2->evlog_fetch_timer = ecore_timer_add(0.2, _cb_evlog, c2);
222 snprintf(buf, sizeof(buf), "%s/efl_debug_evlog-%d.log",
223 getenv("HOME"), c2->pid);
224 c2->evlog_file = fopen(buf, "wb");
225 DBG("client %p (%p) [pid:%d] logging to %s [%p]",
226 c2, c2->client, c2->pid, buf, c2->evlog_file);
227 }
228 }
229 }
230 } 194 }
231 else if (IS_OP(EVOF)) 195 return EINA_TRUE;
196}
197
198static int
199_opcode_register(const char *op_name, int op_id, Opcode_Cb cb)
200{
201 static int free_opcode = 0;
202 Opcode_Information *op_info = eina_hash_find(_string_to_opcode_hash, op_name);
203 if (!op_info)
232 { 204 {
233 if (payload.len < sizeof(int)) 205 op_info = calloc(1, sizeof(*op_info));
234 fprintf(stderr, "INFO: client %p [pid: %d] sent invalid EVOF\n", c, (int)c->pid); 206 if (op_id == EINA_DEBUG_OPCODE_INVALID)
235 else
236 { 207 {
237 int pid; 208 do
238 memcpy(&pid, payload.bytes, sizeof(int));
239 c2 = _client_pid_find(pid);
240 if (!c2)
241 { 209 {
242 fprintf(stderr, "INFO: client %p [pid: %d] sent EVOF %d: no such client\n", c, (int)c->pid, pid); 210 free_opcode = (free_opcode + 1) % MAX_OPCODES;
243 } 211 op_id = free_opcode;
244 else
245 {
246 c2->evlog_on--;
247 DBG("client %p (%p) [pid:%d] requested EVOF (%d) on %p (%p) [pid:%d]",
248 c, c->client, c->pid,
249 c2->evlog_on,
250 c2, c2->client, c2->pid);
251 if (c2->evlog_on == 0)
252 {
253 send_cli(c2, EVOF, NULL, 0);
254 if (c2->evlog_fetch_timer)
255 {
256 ecore_timer_del(c2->evlog_fetch_timer);
257 c2->evlog_fetch_timer = NULL;
258 }
259 if (c2->evlog_file)
260 {
261 DBG("client %p (%p) [pid:%d] finished logged to %p",
262 c2, c2->client, c2->pid, c2->evlog_file);
263 fclose(c2->evlog_file);
264 c2->evlog_file = NULL;
265 }
266 }
267 else if (c2->evlog_on < 0)
268 c2->evlog_on = 0;
269 } 212 }
213 while(_opcodes[op_id]);
270 } 214 }
215 op_info->opcode = op_id;
216 op_info->opcode_string = eina_stringshare_add(op_name);
217 op_info->cb = cb;
218 eina_hash_add(_string_to_opcode_hash, op_name, op_info);
219 _opcodes[op_id] = op_info;
271 } 220 }
272 else if (IS_OP(EVLG)) 221 printf("Register %s -> opcode %d\n", op_name, op_info->opcode);
273 { 222 return op_info->opcode;
274 if (payload.len < sizeof(int)) 223}
275 fprintf(stderr, "INFO: client %p [pid: %d] sent invalid EVLG\n", c, (int)c->pid);
276 else if (!c->evlog_file)
277 fprintf(stderr, "INFO: client %p [pid: %d] no matching EVON\n", c, (int)c->pid);
278 else
279 {
280 unsigned int blocksize = payload.len - sizeof(int);
281 if (blocksize > 0)
282 {
283 unsigned int header[3];
284 224
285 header[0] = 0xffee211; 225static Eina_Bool
286 header[1] = blocksize; 226_hello_cb(Client *c, void *buffer, int size)
287 memcpy(header + 2, payload.mem, sizeof(int)); 227{
228 Eina_List *itr;
229 char *buf = (char *)buffer, *tmp;
288 230
289 if ((fwrite(header, 12, 1, c->evlog_file) != 1) || 231 EXTRACT(buf, &c->version, 4);
290 (fwrite(payload.bytes + sizeof(int), blocksize, 1, c->evlog_file) != 1)) 232 EXTRACT(buf, &c->pid, 4);
291 { 233 size -= 8;
292 fprintf(stderr, "INFO: failed to write log file for client %p [pid: %d]\n", c, (int)c->pid);
293 fclose(c->evlog_file);
294 c->evlog_file = NULL;
295 c->evlog_on = 0;
296 234
297 send_cli(c, EVOF, NULL, 0); 235 c->cid = _free_cid++;
298 if (c->evlog_fetch_timer) 236 if (size > 1)
299 { 237 {
300 ecore_timer_del(c->evlog_fetch_timer); 238 c->app_name = eina_stringshare_add_length(buf, size);
301 c->evlog_fetch_timer = NULL;
302 }
303 }
304 }
305 }
306 } 239 }
307} 240 printf("Connection from %s: pid %d - name %s\n",
308 241 c->is_master ? "Master" : "Slave",
309static void 242 c->pid, c->app_name);
310_client_data(void *data, const Efl_Event *event) 243
311{ 244 if (c->is_master) return EINA_TRUE;
312 Client *c = data; 245
313 if (!received_data(event->object, _process_command, c)) 246 /* Update the observers */
247 size = 2 * sizeof(int) + (c->app_name ? strlen(c->app_name) : 0) + 1; /* cid + pid + name + \0 */
248 buf = alloca(size);
249 tmp = buf;
250 STORE(tmp, &c->cid, sizeof(int));
251 STORE(tmp, &c->pid, sizeof(int));
252 if (c->app_name)
253 {
254 STORE(tmp, c->app_name, strlen(c->app_name) + 1);
255 }
256 else
257 {
258 char end = '\0';
259 STORE(tmp, &end, 1);
260 }
261 EINA_LIST_FOREACH(_clients, itr, c)
314 { 262 {
315 fprintf(stderr, "INFO: client %p [pid: %d] sent invalid data\n", c, (int)c->pid); 263 if (c->cl_stat_obs) _send(c, _slave_added_opcode, buf, size);
316 if (!efl_io_closer_closed_get(event->object))
317 efl_io_closer_close(event->object);
318 return;
319 } 264 }
265 return EINA_TRUE;
320} 266}
321 267
322static void 268static Eina_Bool
323_client_error(void *data, const Efl_Event *event) 269_cid_get_cb(Client *src, void *buffer, int size EINA_UNUSED)
324{ 270{
325 Client *c = data; 271 int pid = *(int *)buffer;
326 Eina_Error *perr = event->info; 272 Client *c = _client_find_by_pid(pid);
327 WRN("client %p [pid: %d] error: %s", 273 int cid = c ? c->cid : 0;
328 c, (int)c->pid, eina_error_msg_get(*perr)); 274 _send(src, _cid_from_pid_opcode, &cid, sizeof(int));
329 fprintf(stderr, "INFO: client %p [pid: %d] error: %s\n", 275 return EINA_TRUE;
330 c, (int)c->pid, eina_error_msg_get(*perr));
331} 276}
332 277
333static void 278static Eina_Bool
334_client_eos(void *data, const Efl_Event *event EINA_UNUSED) 279_data_test_cb(Client *src, void *buffer, int size)
335{ 280{
336 Client *c = data; 281 printf("Data test: loop packet of %d bytes\n", size);
337 DBG("client %p (%p) [pid: %d] closed, pending read %zu, write %zu", 282 _send(src, _test_loop_opcode, buffer, size);
338 c, c->client, (int)c->pid, 283 return EINA_TRUE;
339 efl_io_buffered_stream_pending_read_get(c->client),
340 efl_io_buffered_stream_pending_write_get(c->client));
341 efl_io_closer_close(c->client);
342} 284}
343 285
344static void 286static Eina_Bool
345_client_write_finished(void *data, const Efl_Event *event EINA_UNUSED) 287_cl_stat_obs_register_cb(Client *src, void *buffer, int size)
346{ 288{
347 Client *c = data; 289 Client *c;
348 DBG("client %p (%p) [pid: %d] finished writing, pending read %zu", 290 if (!src) return EINA_FALSE;
349 c, c->client, (int)c->pid, efl_io_buffered_stream_pending_read_get(c->client)); 291 if (!src->is_master) return EINA_FALSE;
292 if (!src->cl_stat_obs)
293 {
294 Eina_List *itr;
295 src->cl_stat_obs = EINA_TRUE;
296 size = 0;
297 EINA_LIST_FOREACH(_clients, itr, c)
298 {
299 char *tmp;
300 if (c->is_master) continue;
301 size = 2 * sizeof(int) + (c->app_name ? strlen(c->app_name) : 0) + 1;
302 buffer = alloca(size);
303 tmp = buffer;
304 STORE(tmp, &c->cid, sizeof(int));
305 STORE(tmp, &c->pid, sizeof(int));
306 if (c->app_name)
307 {
308 STORE(tmp, c->app_name, strlen(c->app_name) + 1);
309 }
310 else
311 {
312 char end = '\0';
313 STORE(tmp, &end, 1);
314 }
315 _send(src, _slave_added_opcode, buffer, size);
316 }
317 }
318 return EINA_TRUE;
350} 319}
351 320
352static void 321static Eina_Bool
353_client_read_finished(void *data, const Efl_Event *event EINA_UNUSED) 322_opcode_register_cb(Client *src, void *buffer, int size)
354{ 323{
355 Client *c = data; 324 char *buf = (char *)buffer;
356 DBG("client %p (%p) [pid: %d] finished reading, pending write %zu", 325 char *ops_buf = buf;
357 c, c->client, (int)c->pid, efl_io_buffered_stream_pending_write_get(c->client)); 326 int ops_size = size;
358}
359 327
360static Efl_Callback_Array_Item *_client_cbs(void); 328 ops_buf += sizeof(uint64_t);
329 ops_size -= sizeof(uint64_t);
330 int *opcodes = (int *)ops_buf;
361 331
362static void 332 while (ops_size > 0)
363_client_finished(void *data, const Efl_Event *event EINA_UNUSED)
364{
365 Client *c = data;
366
367 clients = eina_list_remove(clients, c);
368 if (c->evlog_fetch_timer)
369 { 333 {
370 ecore_timer_del(c->evlog_fetch_timer); 334 int len = strlen(ops_buf) + 1;
371 c->evlog_fetch_timer = NULL; 335 *opcodes++ = _opcode_register(ops_buf, EINA_DEBUG_OPCODE_INVALID, NULL);
336 ops_buf += len;
337 ops_size -= len;
372 } 338 }
373 if (c->evlog_file)
374 {
375 fclose(c->evlog_file);
376 c->evlog_file = NULL;
377 }
378 efl_event_callback_array_del(c->client, _client_cbs(), c);
379 INF("finished client %p (%p) [pid:%d]", c, c->client, c->pid);
380 efl_unref(c->client);
381 free(c);
382}
383
384EFL_CALLBACKS_ARRAY_DEFINE(_client_cbs,
385 { EFL_IO_READER_EVENT_EOS, _client_eos },
386 { EFL_IO_BUFFERED_STREAM_EVENT_ERROR, _client_error },
387 { EFL_IO_BUFFERED_STREAM_EVENT_READ_FINISHED, _client_read_finished },
388 { EFL_IO_BUFFERED_STREAM_EVENT_WRITE_FINISHED, _client_write_finished },
389 { EFL_IO_BUFFERED_STREAM_EVENT_FINISHED, _client_finished },
390 { EFL_IO_BUFFERED_STREAM_EVENT_SLICE_CHANGED, _client_data });
391 339
392static void 340 _send(src, EINA_DEBUG_OPCODE_REGISTER, buf, (char *)opcodes - (char *)buf);
393_client_add(void *data EINA_UNUSED, const Efl_Event *event)
394{
395 Client *c = calloc(1, sizeof(Client));
396 341
397 EINA_SAFETY_ON_NULL_RETURN(c); 342 return EINA_TRUE;
398 c->client = efl_ref(event->info);
399 clients = eina_list_append(clients, c);
400 efl_event_callback_array_add(c->client, _client_cbs(), c);
401 INF("server %p new client %p (%p)", event->object, c, c->client);
402} 343}
403 344
404static void 345static int
405_error(void *data EINA_UNUSED, const Efl_Event *event) 346_data_receive(Client *c, unsigned char **buffer)
406{ 347{
407 Eina_Error *perr = event->info; 348 unsigned char *recv_buf = NULL;
408 ERR("server %p error: %s", event->object, eina_error_msg_get(*perr)); 349 int rret;
409 fprintf(stderr, "ERROR: %s\n", eina_error_msg_get(*perr)); 350 int size = 0;
410 ecore_main_loop_quit();
411 retval = EXIT_FAILURE;
412}
413 351
414int 352 if (!c) return -1;
415main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
416{
417 Eo *loop;
418 char *path;
419 Eina_Error err;
420 353
421 ecore_app_no_system_modules(); 354 rret = recv(c->fd, &size, sizeof(int), MSG_PEEK);
422 355
423 eina_init(); 356 if (rret == sizeof(int))
424 ecore_init(); 357 {
425 ecore_con_init(); 358 int cur_packet_size = 0;
359 // allocate a buffer for the next bytes to receive
360 recv_buf = malloc(size);
361 if (!recv_buf) goto error;
362 while (cur_packet_size < size)
363 {
364 rret = recv(c->fd, recv_buf + cur_packet_size, size - cur_packet_size, 0);
365 if (rret <= 0) goto error;
366 cur_packet_size += rret;
367 }
368 }
369 if (buffer) *buffer = recv_buf;
370 //printf("%d bytes received from client %s fd %d\n", size, c->app_name, c->fd);
371 return size;
372error:
373 if (rret == -1) perror("Read from socket");
374 if (recv_buf) free(recv_buf);
375 return -1;
376}
426 377
427 _log_dom = eina_log_domain_register("efl_debugd", EINA_COLOR_CYAN); 378static void
379_monitor()
380{
381#ifndef _WIN32
382#define MAX_EVENTS 1000
383 int ret = 0;
384 struct epoll_event events[MAX_EVENTS];
385 Client *c;
428 386
429 path = ecore_con_local_path_new(EINA_FALSE, "efl_debug", 0); 387 // sit forever processing commands or timeouts
430 if (!path) 388 for (; ret != -1;)
431 { 389 {
432 fprintf(stderr, "ERROR: could not get local communication path\n"); 390 ret = epoll_wait (_epfd, events, MAX_EVENTS, -1);
433 retval = EXIT_FAILURE;
434 goto end;
435 }
436 391
437 loop = ecore_main_loop_get(); 392 // if the fd for debug daemon says it's alive, process it
393 if (ret > 0)
394 {
395 int i;
396 //check which fd are set/ready for read
397 for (i = 0; i < ret; i++)
398 {
399 if (events[i].events & EPOLLHUP)
400 {
401 c = _client_find_by_fd(events[i].data.fd);
402 close(events[i].data.fd);
403 if (c) _client_del(c);
404 }
405 if (events[i].events & EPOLLIN)
406 {
407 // Someone wants to connect
408 if(events[i].data.fd == _listening_master_fd || events[i].data.fd == _listening_slave_fd)
409 {
410 int new_fd = accept(events[i].data.fd, NULL, NULL);
411 if (new_fd < 0) perror("Accept");
412 else
413 {
414 struct epoll_event event;
415 c = calloc(1, sizeof(*c));
416 c->fd = new_fd;
417 c->is_master = (events[i].data.fd == _listening_master_fd);
418 _clients = eina_list_append(_clients, c);
419 event.data.fd = new_fd;
420 event.events = EPOLLIN;
421 epoll_ctl (_epfd, EPOLL_CTL_ADD, new_fd, &event);
422 }
423 continue;
424 }
438 425
439#ifdef EFL_NET_SERVER_UNIX_CLASS 426 c = _client_find_by_fd(events[i].data.fd);
440 server = efl_add(EFL_NET_SERVER_SIMPLE_CLASS, loop, 427 if (c)
441 efl_net_server_simple_inner_class_set(efl_added, EFL_NET_SERVER_UNIX_CLASS)); 428 {
442#else 429 int size;
443 /* TODO: maybe start a TCP using locahost:12345? 430 unsigned char *buffer;
444 * Right now eina_debug_monitor is only for AF_UNIX, so not an issue. 431 size = _data_receive(c, &buffer);
445 */ 432 // if not negative - we have a real message
446 fprintf(stderr, "ERROR: your platform doesn't support Efl.Net.Server.Unix\n"); 433 if (size > 0)
434 {
435 if(!_dispatch(c, buffer, size))
436 {
437 // something we don't understand
438 fprintf(stderr, "Dispatch: unknown command");
439 }
440 free(buffer);
441 }
442 else
443 {
444 // major failure on debug daemon control fd - get out of here.
445 // else goto fail;
446 close(events[i].data.fd);
447 //TODO if its not main session we will tell the main_loop
448 //that it disconneted
449 }
450 }
451 }
452 }
453 }
454#if 0
455 else
456 {
457 if (poll_time && poll_timer_cb)
458 {
459 if (!poll_timer_cb()) poll_time = 0;
460 }
461 }
447#endif 462#endif
448 if (!server)
449 {
450 fprintf(stderr, "ERROR: could not create communication server\n");
451 retval = EXIT_FAILURE;
452 goto end;
453 } 463 }
464#endif
465}
454 466
455 efl_event_callback_add(server, EFL_NET_SERVER_EVENT_CLIENT_ADD, _client_add, NULL); 467static const char *
456 efl_event_callback_add(server, EFL_NET_SERVER_EVENT_ERROR, _error, NULL); 468_socket_home_get()
469{
470 // get possible debug daemon socket directory base
471 const char *dir = getenv("XDG_RUNTIME_DIR");
472 if (!dir) dir = eina_environment_home_get();
473 if (!dir) dir = eina_environment_tmp_get();
474 return dir;
475}
457 476
458#ifdef EFL_NET_SERVER_UNIX_CLASS 477#ifndef _WIN32
459 { 478#define LENGTH_OF_SOCKADDR_UN(s) \
460 Eo *inner_server = efl_net_server_simple_inner_server_get(server); 479 (strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
461 efl_net_server_unix_unlink_before_bind_set(inner_server, EINA_TRUE); 480static int
462 efl_net_server_unix_leading_directories_create_set(inner_server, EINA_TRUE, 0700); 481_local_listening_socket_create(const char *path)
463 } 482{
483 struct sockaddr_un socket_unix;
484 int socket_unix_len, curstate = 0;
485 // create the socket
486 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
487 if (fd < 0) goto err;
488 // set the socket to close when we exec things so they don't inherit it
489 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) goto err;
490 // set up some socket options on addr re-use
491 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate,
492 sizeof(curstate)) < 0)
493 goto err;
494 // sa that it's a unix socket and where the path is
495 socket_unix.sun_family = AF_UNIX;
496 strncpy(socket_unix.sun_path, path, sizeof(socket_unix.sun_path) - 1);
497 socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
498 unlink(socket_unix.sun_path);
499 if (bind(fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
500 {
501 perror("ERROR on binding");
502 goto err;
503 }
504 listen(fd, 5);
505 return fd;
506err:
507 if (fd >= 0) close(fd);
508 return -1;
509}
464#endif 510#endif
465 511
466 err = efl_net_server_serve(server, path); 512static Eina_Bool
467 if (err) 513_server_launch()
514{
515#ifndef _WIN32
516 char buf[4096];
517 struct epoll_event event = {0};
518 mode_t mask = 0;
519 const char *socket_home_path = _socket_home_get();
520 char *socket_path = NULL;
521 if (!socket_home_path) return EINA_FALSE;
522 _epfd = epoll_create (MAX_EVENTS);
523 socket_path = strdup(socket_home_path);
524
525 snprintf(buf, sizeof(buf), "%s/%s", socket_path, SERVER_PATH);
526 if (mkdir(buf, S_IRWXU) < 0 && errno != EEXIST)
527 {
528 perror("mkdir SERVER_PATH");
529 goto err;
530 }
531 snprintf(buf, sizeof(buf), "%s/%s/%s", socket_path, SERVER_PATH, SERVER_NAME);
532 if (mkdir(buf, S_IRWXU) < 0 && errno != EEXIST)
468 { 533 {
469 fprintf(stderr, "ERROR: could not serve '%s': %s\n", path, eina_error_msg_get(err)); 534 perror("mkdir SERVER_NAME");
470 retval = EXIT_FAILURE; 535 goto err;
471 goto end;
472 } 536 }
537 mask = umask(S_IRWXG | S_IRWXO);
538 snprintf(buf, sizeof(buf), "%s/%s/%s/%i", socket_path, SERVER_PATH, SERVER_NAME, SERVER_MASTER_PORT);
539 _listening_master_fd = _local_listening_socket_create(buf);
540 if (_listening_master_fd <= 0) goto err;
541 event.data.fd = _listening_master_fd;
542 event.events = EPOLLIN;
543 epoll_ctl (_epfd, EPOLL_CTL_ADD, _listening_master_fd, &event);
544 snprintf(buf, sizeof(buf), "%s/%s/%s/%i", socket_path, SERVER_PATH, SERVER_NAME, SERVER_SLAVE_PORT);
545 _listening_slave_fd = _local_listening_socket_create(buf);
546 if (_listening_slave_fd <= 0) goto err;
547 event.data.fd = _listening_slave_fd;
548 event.events = EPOLLIN;
549 epoll_ctl (_epfd, EPOLL_CTL_ADD, _listening_slave_fd, &event);
550 umask(mask);
551 return EINA_TRUE;
552err:
553 if (mask) umask(mask);
554 if (_listening_master_fd >= 0) close(_listening_master_fd);
555 _listening_master_fd = -1;
556 if (_listening_slave_fd >= 0) close(_listening_slave_fd);
557 _listening_slave_fd = -1;
558 free(socket_path);
559#endif
560 return EINA_FALSE;
561}
562
563int
564main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
565{
566 eina_debug_disable();
567 eina_init();
568 ecore_init();
473 569
474 ecore_main_loop_begin(); 570 _string_to_opcode_hash = eina_hash_string_superfast_new(NULL);
571 _opcode_register("daemon/opcode/register", EINA_DEBUG_OPCODE_REGISTER, _opcode_register_cb);
572 _opcode_register("daemon/greet", EINA_DEBUG_OPCODE_HELLO, _hello_cb);
573 _clients_stat_register_opcode = _opcode_register("daemon/observer/client/register", EINA_DEBUG_OPCODE_INVALID, _cl_stat_obs_register_cb);
574 _slave_added_opcode = _opcode_register("daemon/observer/slave_added", EINA_DEBUG_OPCODE_INVALID, NULL);
575 _slave_deleted_opcode = _opcode_register("daemon/observer/slave_deleted", EINA_DEBUG_OPCODE_INVALID, NULL);
576 _cid_from_pid_opcode = _opcode_register("daemon/info/cid_from_pid", EINA_DEBUG_OPCODE_INVALID, _cid_get_cb);
577 _test_loop_opcode = _opcode_register("daemon/test/loop", EINA_DEBUG_OPCODE_INVALID, _data_test_cb);
475 578
476 end: 579 _server_launch();
477 efl_del(server); 580 _monitor();
478 free(path);
479 581
480 ecore_con_shutdown();
481 ecore_shutdown(); 582 ecore_shutdown();
482 eina_shutdown(); 583 eina_shutdown();
483 584
484 return retval; 585 return 0;
485} 586}