2 * Copyright 2012 Samsung Electronics Co., Ltd
4 * Licensed under the Flora License, Version 1.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
8 * http://www.tizenopensource.org/license
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.
26 #include <secom_socket.h>
29 #include <sys/socket.h>
30 #include <sys/ioctl.h>
34 #define EAPI __attribute__((visibility("default")))
43 request_cb_t request_cb;
50 result_cb_t result_cb;
57 pthread_mutex_t server_mutex;
59 const char *socket_file;
60 struct server_cb server_cb;
63 .server_mutex = PTHREAD_MUTEX_INITIALIZER,
65 .socket_file = "/tmp/.shortcut",
78 PACKET_MAX = 0xFF, /* MAX */
105 struct connection_state {
107 struct packet packet;
123 gboolean check_reply_service(int conn_fd, struct connection_state *state)
125 struct client_cb *client_cb = state->data;
127 if (client_cb->result_cb) {
128 /* If the callback return FAILURE,
129 * remove itself from the callback list */
130 client_cb->result_cb(state->packet.head.data.ack.ret,
131 state->from_pid, client_cb->data);
134 state->state = BEGIN;
138 /* NOTE: If we want close the connection, returns FALSE */
145 gboolean do_reply_service(int conn_fd, struct connection_state *state)
148 struct packet send_packet;
151 if (s_info.server_cb.request_cb) {
157 if (state->packet.head.data.req.field_size.pkgname)
158 pkgname = state->payload;
162 if (state->packet.head.data.req.field_size.name)
163 name = state->payload + state->packet.head.data.req.field_size.pkgname;
167 if (state->packet.head.data.req.field_size.exec) {
168 exec = state->payload + state->packet.head.data.req.field_size.pkgname;
169 exec += state->packet.head.data.req.field_size.name;
174 if (state->packet.head.data.req.field_size.icon) {
175 icon = state->payload + state->packet.head.data.req.field_size.pkgname;
176 icon += state->packet.head.data.req.field_size.name;
177 icon += state->packet.head.data.req.field_size.exec;
182 LOGD("Pkgname: [%s] Type: [%x], Name: [%s], Exec: [%s], Icon: [%s]\n",
184 state->packet.head.data.req.shortcut_type,
189 ret = s_info.server_cb.request_cb(
192 state->packet.head.data.req.shortcut_type,
196 s_info.server_cb.data);
199 send_packet.head.type = PACKET_ACK;
200 send_packet.head.payload_size = 0;
201 send_packet.head.seq = state->packet.head.seq;
202 send_packet.head.data.ack.ret = ret;
204 if (secom_send(conn_fd, (const char*)&send_packet, sizeof(send_packet)) != sizeof(send_packet)) {
205 LOGE("Faield to send ack packet\n");
209 state->state = BEGIN;
218 gboolean filling_payload(int conn_fd, struct connection_state *state)
220 if (state->length < state->packet.head.payload_size) {
224 ret = secom_recv(conn_fd,
225 state->payload + state->length,
226 state->packet.head.payload_size - state->length,
231 if (state->from_pid != check_pid) {
232 LOGD("PID is not matched (%d, expected %d)\n",
233 check_pid, state->from_pid);
237 state->length += ret;
240 if (state->length == state->packet.head.payload_size) {
251 gboolean deal_error_packet(int conn_fd, struct connection_state *state)
253 if (state->length < state->packet.head.payload_size) {
258 buf = alloca(state->packet.head.payload_size - state->length);
260 ret = secom_recv(conn_fd,
262 state->packet.head.payload_size - state->length,
267 if (check_pid != state->from_pid)
268 LOGD("PID is not matched (%d, expected %d)\n",
269 check_pid, state->from_pid);
271 state->length += ret;
274 if (state->length < state->packet.head.payload_size)
277 /* Now, drop this connection */
284 gboolean filling_header(int conn_fd, struct connection_state *state)
286 if (state->length < sizeof(state->packet)) {
290 ret = secom_recv(conn_fd,
291 ((char*)&state->packet) + state->length,
292 sizeof(state->packet) - state->length,
297 if (state->from_pid == 0)
298 state->from_pid = check_pid;
300 if (state->from_pid != check_pid) {
301 LOGD("PID is not matched (%d, expected %d)\n",
302 check_pid, state->from_pid);
306 state->length += ret;
309 if (state->length < sizeof(state->packet))
312 if (state->packet.head.type == PACKET_ACK) {
315 if (state->packet.head.payload_size)
316 state->state = ERROR;
319 } else if (state->packet.head.type == PACKET_REQ) {
320 /* Let's take the next part. */
321 state->state = PAYLOAD;
324 state->payload = calloc(1, state->packet.head.payload_size);
325 if (!state->payload) {
326 LOGE("Heap: %s\n", strerror(errno));
330 LOGE("Invalid packet type\n");
340 gboolean client_connection_cb(GIOChannel *src, GIOCondition cond, gpointer data)
345 struct connection_state *state = data;
346 struct client_cb *client_cb = state->data;
348 if (!(cond & G_IO_IN)) {
349 LOGE("Condition value is unexpected value\n");
354 conn_fd = g_io_channel_unix_get_fd(src);
356 if (ioctl(conn_fd, FIONREAD, &read_size) < 0) {
357 LOGE("Failed to get q size\n");
362 if (read_size == 0) {
363 if (client_cb->result_cb) {
364 /* If the callback return FAILURE,
365 * remove itself from the callback list */
366 client_cb->result_cb(-ECONNABORTED,
367 state->from_pid, client_cb->data);
373 switch (state->state) {
375 state->state = HEADER;
377 ret = filling_header(conn_fd, state);
380 ret = deal_error_packet(conn_fd, state);
387 if (state->state == END)
388 ret = check_reply_service(conn_fd, state);
404 gboolean connection_cb(GIOChannel *src, GIOCondition cond, gpointer data)
407 struct connection_state *state = data;
411 if (!(cond & G_IO_IN)) {
416 conn_fd = g_io_channel_unix_get_fd(src);
418 if (ioctl(conn_fd, FIONREAD, &read_size) < 0) {
419 LOGE("Failed to get q size\n");
424 if (read_size == 0) {
425 LOGE("Disconnected\n");
430 switch (state->state) {
432 state->state = HEADER;
434 ret = filling_header(conn_fd, state);
437 ret = filling_payload(conn_fd, state);
440 LOGE("[%s:%d] Invalid state(%x)\n",
441 __func__, __LINE__, state->state);
446 if (state->state == END) {
447 ret = do_reply_service(conn_fd, state);
448 if (state->payload) {
449 free(state->payload);
450 state->payload = NULL;
453 memset(&state->packet, 0, sizeof(state->packet));
459 secom_put_connection_handle(conn_fd);
462 free(state->payload);
473 gboolean accept_cb(GIOChannel *src, GIOCondition cond, gpointer data)
479 struct connection_state *state;
481 server_fd = g_io_channel_unix_get_fd(src);
482 if (server_fd != s_info.server_fd) {
483 LOGE("Unknown FD is gotten.\n");
485 * In this case, don't try to do anything.
486 * This is not recoverble error */
490 if (!(cond & G_IO_IN)) {
491 close(s_info.server_fd);
492 if (pthread_mutex_lock(&s_info.server_mutex) != 0)
493 LOGE("[%s:%d] Failed to get lock (%s)\n",
494 __func__, __LINE__, strerror(errno));
495 s_info.server_fd = -1;
496 if (pthread_mutex_unlock(&s_info.server_mutex) != 0)
497 LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
498 __func__, __LINE__, strerror(errno));
502 connection_fd = secom_get_connection_handle(server_fd);
503 if (connection_fd < 0) {
504 /* Error log will be printed from
505 * get_connection_handle function */
509 if (fcntl(connection_fd, F_SETFD, FD_CLOEXEC) < 0)
510 LOGE("Error: %s\n", strerror(errno));
512 if (fcntl(connection_fd, F_SETFL, O_NONBLOCK) < 0)
513 LOGE("Error: %s\n", strerror(errno));
515 gio = g_io_channel_unix_new(connection_fd);
517 LOGE("Failed to create a new connection channel\n");
518 secom_put_connection_handle(connection_fd);
522 state = calloc(1, sizeof(*state));
524 LOGE("Heap: %s\n", strerror(errno));
525 secom_put_connection_handle(connection_fd);
529 state->state = BEGIN;
530 id = g_io_add_watch(gio,
531 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
532 (GIOFunc)connection_cb, state);
535 LOGE("Failed to create g_io watch\n");
537 g_io_channel_unref(gio);
538 g_io_channel_shutdown(gio, TRUE, &err);
539 secom_put_connection_handle(connection_fd);
543 g_io_channel_unref(gio);
549 static inline int init_client(struct client_cb *client_cb,
550 const char *packet, int packet_size)
555 struct connection_state *state;
557 client_fd = secom_create_client(s_info.socket_file);
559 LOGE("Failed to make the client FD\n");
563 if (fcntl(client_fd, F_SETFD, FD_CLOEXEC) < 0)
564 LOGE("Error: %s\n", strerror(errno));
566 if (fcntl(client_fd, F_SETFL, O_NONBLOCK) < 0)
567 LOGE("Error: %s\n", strerror(errno));
569 gio = g_io_channel_unix_new(client_fd);
575 state = calloc(1, sizeof(*state));
577 LOGE("Error: %s\n", strerror(errno));
582 state->data = client_cb;
584 id = g_io_add_watch(gio,
585 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
586 (GIOFunc)client_connection_cb, state);
589 LOGE("Failed to create g_io watch\n");
591 g_io_channel_unref(gio);
592 g_io_channel_shutdown(gio, TRUE, &err);
597 g_io_channel_unref(gio);
599 if (secom_send(client_fd, packet, packet_size) != packet_size) {
601 LOGE("Failed to send all packet "
602 "(g_io_channel is not cleared now\n");
604 g_io_channel_shutdown(gio, TRUE, &err);
615 int init_server(void)
620 if (s_info.server_fd != -1) {
621 LOGE("Already initialized\n");
625 if (pthread_mutex_lock(&s_info.server_mutex) != 0) {
626 LOGE("[%s:%d] Failed to get lock (%s)\n",
627 __func__, __LINE__, strerror(errno));
631 unlink(s_info.socket_file);
632 s_info.server_fd = secom_create_server(s_info.socket_file);
634 if (s_info.server_fd < 0) {
635 LOGE("Failed to open a socket (%s)\n", strerror(errno));
636 if (pthread_mutex_unlock(&s_info.server_mutex) != 0)
637 LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
638 __func__, __LINE__, strerror(errno));
642 if (fcntl(s_info.server_fd, F_SETFD, FD_CLOEXEC) < 0)
643 LOGE("Error: %s\n", strerror(errno));
645 if (fcntl(s_info.server_fd, F_SETFL, O_NONBLOCK) < 0)
646 LOGE("Error: %s\n", strerror(errno));
648 gio = g_io_channel_unix_new(s_info.server_fd);
650 close(s_info.server_fd);
651 s_info.server_fd = -1;
652 if (pthread_mutex_unlock(&s_info.server_mutex) != 0)
653 LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
654 __func__, __LINE__, strerror(errno));
658 id = g_io_add_watch(gio,
659 G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
660 (GIOFunc)accept_cb, NULL);
663 LOGE("Failed to create g_io watch\n");
664 g_io_channel_unref(gio);
665 g_io_channel_shutdown(gio, TRUE, &err);
666 close(s_info.server_fd);
667 s_info.server_fd = -1;
668 if (pthread_mutex_unlock(&s_info.server_mutex) != 0)
669 LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
670 __func__, __LINE__, strerror(errno));
675 g_io_channel_unref(gio);
677 if (pthread_mutex_unlock(&s_info.server_mutex) != 0) {
679 g_io_channel_shutdown(gio, TRUE, &err);
680 LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
681 __func__, __LINE__, strerror(errno));
683 * We couldn't make a lock for this statements.
684 * We already meet the unrecoverble error
686 close(s_info.server_fd);
687 s_info.server_fd = -1;
696 EAPI int shortcut_set_request_cb(request_cb_t request_cb, void *data)
699 s_info.server_cb.request_cb = request_cb;
700 s_info.server_cb.data = data;
704 LOGE("Failed to initialize the server\n");
712 EAPI int add_to_home_shortcut(const char *pkgname, const char *name, int type, const char *content_info, const char *icon, result_cb_t result_cb, void *data)
714 struct packet *packet;
721 struct client_cb *client_cb;
724 pkgname_len = pkgname ? strlen(pkgname) + 1 : 0;
725 name_len = name ? strlen(name) + 1 : 0;
726 exec_len = content_info ? strlen(content_info) + 1 : 0;
727 icon_len = icon ? strlen(icon) + 1 : 0;
729 packet_size = sizeof(*packet) + name_len + exec_len + icon_len + pkgname_len + 1;
731 packet = alloca(packet_size);
733 LOGE("Heap: %s\n", strerror(errno));
737 packet->head.seq = s_info.seq++;
738 packet->head.type = PACKET_REQ;
739 packet->head.data.req.shortcut_type = type;
740 packet->head.data.req.field_size.pkgname = pkgname_len;
741 packet->head.data.req.field_size.name = name_len;
742 packet->head.data.req.field_size.exec = exec_len;
743 packet->head.data.req.field_size.icon = icon_len;
744 packet->head.payload_size = pkgname_len + name_len + exec_len + icon_len + 1;
746 payload = packet->payload;
749 strncpy(payload, pkgname, pkgname_len);
750 payload += pkgname_len;
754 strncpy(payload, name, name_len);
759 strncpy(payload, content_info, exec_len);
764 strncpy(payload, icon, icon_len);
766 client_cb = malloc(sizeof(*client_cb));
768 LOGE("Heap: %s\n", strerror(errno));
772 client_cb->result_cb = result_cb;
773 client_cb->data = data;
775 if (init_client(client_cb, (const char*)packet, packet_size) < 0) {
776 LOGE("Failed to init client FD\n");
784 EAPI int shortcut_add_to_home(const char *pkgname, const char *name, int type, const char *content_info, const char *icon, result_cb_t result_cb, void *data)
786 return add_to_home_shortcut(pkgname, name, type, content_info, icon, result_cb, data);