#include <errno.h>
#include <string.h>
#include <stdlib.h>
+#include <pthread.h>
#include <dlog.h>
#include <glib.h>
#include <com-core.h>
#include <com-core_packet.h>
#include <livebox-errno.h>
+#include <secure_socket.h>
#include "debug.h"
#include "client.h"
#include "master_rpc.h"
#include "conf.h"
#include "critical_log.h"
+#include "file_service.h"
+
+int errno;
static struct info {
int fd;
lb_set_lb_fb(handler, fbfile);
ret = fb_sync(lb_get_lb_fb(handler));
- if (ret != LB_STATUS_SUCCESS)
+ if (ret != LB_STATUS_SUCCESS) {
ErrPrint("Failed to do sync FB (%s - %s) (%d)\n", pkgname, fbfile, ret);
- else
+ } else {
lb_invoke_event_handler(handler, LB_EVENT_LB_UPDATE_BEGIN);
+ }
} else {
ErrPrint("Invalid request[%s], %s\n", id, fbfile);
}
lb_set_lb_fb(handler, fbfile);
ret = fb_sync(lb_get_lb_fb(handler));
- if (ret != LB_STATUS_SUCCESS)
+ if (ret != LB_STATUS_SUCCESS) {
ErrPrint("Failed to do sync FB (%s - %s) (%d)\n", pkgname, fbfile, ret);
- else
+ } else {
lb_invoke_event_handler(handler, LB_EVENT_PD_UPDATE_BEGIN);
+ }
} else {
ErrPrint("Invalid request[%s], %s\n", id, fbfile);
}
} else if (lb_get_lb_fb(handler)) {
lb_set_lb_fb(handler, fbfile);
ret = fb_sync(lb_get_lb_fb(handler));
- if (ret != LB_STATUS_SUCCESS)
+ if (ret != LB_STATUS_SUCCESS) {
ErrPrint("Failed to do sync FB (%s - %s) (%d)\n", pkgname, util_basename(util_uri_to_path(id)), ret);
+ }
} else {
ret = LB_STATUS_SUCCESS;
}
- if (ret == LB_STATUS_SUCCESS)
+ if (ret == LB_STATUS_SUCCESS) {
lb_invoke_event_handler(handler, LB_EVENT_LB_UPDATED);
+ }
out:
return NULL;
} else {
(void)lb_set_pd_fb(handler, buf_id);
ret = fb_sync(lb_get_pd_fb(handler));
- if (ret < 0)
+ if (ret < 0) {
ErrPrint("Failed to do sync FB (%s - %s)\n", pkgname, util_basename(util_uri_to_path(id)));
+ }
}
handler->is_pd_created = (status == 0);
(void)lb_set_pd_fb(handler, fbfile);
ret = fb_sync(lb_get_pd_fb(handler));
- if (ret < 0)
+ if (ret < 0) {
ErrPrint("Failed to do sync FB (%s - %s), %d\n", pkgname, util_basename(util_uri_to_path(id)), ret);
- else
+ } else {
lb_invoke_event_handler(handler, LB_EVENT_PD_UPDATED);
+ }
}
out:
goto out;
}
- if (status == LB_STATUS_SUCCESS)
+ if (status == LB_STATUS_SUCCESS) {
lb_set_update_mode(handler, active_mode);
+ }
if (handler->update_mode_cb) {
ret_cb_t cb;
lb_set_lb_fb(handler, fbfile);
ret = fb_sync(lb_get_lb_fb(handler));
- if (ret < 0)
+ if (ret < 0) {
ErrPrint("Failed to do sync FB (%s - %s)\n", pkgname, util_basename(util_uri_to_path(id)));
+ }
/* Just update the size info only. */
}
goto out;
}
- if (status == 0)
+ if (status == 0) {
lb_set_period(handler, period);
+ }
if (handler->period_changed_cb) {
ret_cb_t cb;
goto out;
}
- if (status == 0)
+ if (status == 0) {
(void)lb_set_group(handler, cluster, category);
+ }
if (handler->group_changed_cb) {
ret_cb_t cb;
break;
case _LB_TYPE_SCRIPT:
case _LB_TYPE_BUFFER:
- if (!strlen(lb_fname))
+ if (!strlen(lb_fname)) {
break;
+ }
lb_set_lb_fb(handler, lb_fname);
ret = fb_sync(lb_get_lb_fb(handler));
- if (ret < 0)
+ if (ret < 0) {
ErrPrint("Failed to do sync FB (%s - %s)\n", pkgname, util_basename(util_uri_to_path(id)));
+ }
break;
case _LB_TYPE_TEXT:
lb_set_text_lb(handler);
switch (pd_type) {
case _PD_TYPE_SCRIPT:
case _PD_TYPE_BUFFER:
- if (!strlen(pd_fname))
+ if (!strlen(pd_fname)) {
break;
+ }
lb_set_pd_fb(handler, pd_fname);
ret = fb_sync(lb_get_pd_fb(handler));
- if (ret < 0)
+ if (ret < 0) {
ErrPrint("Failed to do sync FB (%s - %s)\n", pkgname, util_basename(util_uri_to_path(id)));
+ }
break;
case _PD_TYPE_TEXT:
lb_set_text_pd(handler);
} else {
int ret;
- if (packet_get(result, "i", &ret) != 1)
+ if (packet_get(result, "i", &ret) != 1) {
ErrPrint("Invalid argument\n");
- else
+ } else {
DbgPrint("Acquire returns: %d\n", ret);
+ }
}
return;
}
}
+ (void)file_service_init();
+
DbgPrint("Server Address: %s\n", s_info.client_addr);
com_core_add_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL);
com_core_add_event_callback(CONNECTOR_CONNECTED, connected_cb, NULL);
- if (vconf_notify_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb, NULL) < 0)
+ if (vconf_notify_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb, NULL) < 0) {
ErrPrint("Failed to add vconf for service state\n");
- else
+ } else {
DbgPrint("vconf event callback is registered\n");
+ }
master_started_cb(NULL, NULL);
return 0;
int client_fini(void)
{
int ret;
+
+ (void)file_service_fini();
+
ret = vconf_ignore_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb);
- if (ret < 0)
+ if (ret < 0) {
DbgPrint("Ignore vconf key: %d\n", ret);
+ }
+
com_core_del_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL);
com_core_del_event_callback(CONNECTOR_CONNECTED, connected_cb, NULL);
com_core_packet_client_fini(s_info.fd);
s_info.fd = -1;
+ free(s_info.client_addr);
+ s_info.client_addr = NULL;
return LB_STATUS_SUCCESS;
}
}
ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
- if (ops->update_text)
+ if (ops->update_text) {
ops->update_text(handle, (const char *)block->id, (const char *)block->part, (const char *)block->data);
+ }
return 0;
}
}
ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
- if (ops->update_image)
+ if (ops->update_image) {
ops->update_image(handle, block->id, block->part, block->data, block->option);
+ }
return 0;
}
}
ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
- if (ops->update_script)
+ if (ops->update_script) {
ops->update_script(handle, block->id, block->part, block->data, block->option);
+ }
return 0;
}
}
ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
- if (ops->update_signal)
+ if (ops->update_signal) {
ops->update_signal(handle, block->id, block->data, block->part);
+ }
return 0;
}
return LB_STATUS_ERROR_INVALID;
}
- if (ops->update_drag)
+ if (ops->update_drag) {
ops->update_drag(handle, block->id, block->part, dx, dy);
+ }
return 0;
}
return LB_STATUS_ERROR_INVALID;
}
- if (ops->update_info_size)
+ if (ops->update_info_size) {
ops->update_info_size(handle, block->id, w, h);
-
+ }
} else if (!strcasecmp(block->part, INFO_CATEGORY)) {
- if (ops->update_info_category)
+ if (ops->update_info_category) {
ops->update_info_category(handle, block->id, block->data);
+ }
}
return 0;
ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
- if (ops->update_begin)
+ if (ops->update_begin) {
ops->update_begin(handle);
+ }
return 0;
}
ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
- if (ops->update_end)
+ if (ops->update_end) {
ops->update_end(handle);
+ }
return 0;
}
if (!isspace(ch)) {
update_end(handle, is_pd);
- fclose(fp);
+ if (fclose(fp) != 0) {
+ ErrPrint("fclose: %s\n", strerror(errno));
+ }
return LB_STATUS_ERROR_INVALID;
}
break;
case BLOCK_OPEN:
- if (isblank(ch))
+ if (isblank(ch)) {
break;
+ }
- if (ch != '\n')
+ if (ch != '\n') {
goto errout;
+ }
block = calloc(1, sizeof(*block));
if (!block) {
CRITICAL_LOG("Heap: %s\n", strerror(errno));
update_end(handle, is_pd);
- fclose(fp);
+ if (fclose(fp) != 0) {
+ ErrPrint("fclose: %s\n", strerror(errno));
+ }
return LB_STATUS_ERROR_MEMORY;
}
break;
case FIELD:
- if (isspace(ch))
+ if (isspace(ch)) {
break;
+ }
if (ch == '}') {
state = BLOCK_CLOSE;
}
if (ch == '=') {
- if (field_name[field_idx][idx] != '\0')
+ if (field_name[field_idx][idx] != '\0') {
goto errout;
+ }
switch (field_idx) {
case 0:
break;
}
- if (ch == '\n')
+ if (ch == '\n') {
goto errout;
+ }
if (field_name[field_idx][idx] != ch) {
- ungetc(ch, fp);
- while (--idx >= 0)
- ungetc(field_name[field_idx][idx], fp);
+ if (ungetc(ch, fp) != ch) {
+ ErrPrint("ungetc: %s\n", strerror(errno));
+ }
+
+ while (--idx >= 0) {
+ if (ungetc(field_name[field_idx][idx], fp) != field_name[field_idx][idx]) {
+ ErrPrint("ungetc: %s\n", strerror(errno));
+ }
+ }
field_idx++;
- if (field_name[field_idx] == NULL)
+ if (field_name[field_idx] == NULL) {
goto errout;
+ }
idx = 0;
break;
case BLOCK_CLOSE:
if (!block->file) {
block->file = strdup(util_uri_to_path(handle->id));
- if (!block->file)
+ if (!block->file) {
goto errout;
+ }
}
i = 0;
i++;
}
- if (!handlers[i].type)
+ if (!handlers[i].type) {
ErrPrint("Unknown block type: %s\n", block->type);
+ }
free(block->file);
free(block->type);
} /* switch */
} /* while */
- if (state != UNKNOWN)
+ if (state != UNKNOWN) {
goto errout;
+ }
update_end(handle, is_pd);
- fclose(fp);
+ if (fclose(fp) != 0) {
+ ErrPrint("fclose: %s\n", strerror(errno));
+ }
return 0;
errout:
update_end(handle, is_pd);
- fclose(fp);
+ if (fclose(fp) != 0) {
+ ErrPrint("fclose: %s\n", strerror(errno));
+ }
return LB_STATUS_ERROR_INVALID;
}
--- /dev/null
+/*
+ * Copyright 2013 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include <dlog.h>
+#include <secure_socket.h>
+#include <livebox-errno.h>
+
+#include "client.h"
+#include "debug.h"
+#include "dlist.h"
+
+#define FILE_SERVICE_PORT 8209
+
+#define CRITICAL_SECTION_BEGIN(handle) \
+do { \
+ int ret; \
+ ret = pthread_mutex_lock(handle); \
+ if (ret != 0) \
+ ErrPrint("Failed to lock: %s\n", strerror(ret)); \
+} while (0)
+
+#define CRITICAL_SECTION_END(handle) \
+do { \
+ int ret; \
+ ret = pthread_mutex_unlock(handle); \
+ if (ret != 0) \
+ ErrPrint("Failed to unlock: %s\n", strerror(ret)); \
+} while (0)
+
+#define CANCEL_SECTION_BEGIN() do { \
+ int ret; \
+ ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); \
+ if (ret != 0) \
+ ErrPrint("Unable to set cancelate state: %s\n", strerror(ret)); \
+} while (0)
+
+#define CANCEL_SECTION_END() do { \
+ int ret; \
+ ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); \
+ if (ret != 0) \
+ ErrPrint("Unable to set cancelate state: %s\n", strerror(ret)); \
+} while (0)
+
+#define CLOSE_PIPE(p) do { \
+ int status; \
+ status = close(p[PIPE_READ]); \
+ if (status < 0) \
+ ErrPrint("close: %s\n", strerror(errno)); \
+ status = close(p[PIPE_WRITE]); \
+ if (status < 0) \
+ ErrPrint("close: %s\n", strerror(errno)); \
+} while (0)
+
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+#define PIPE_MAX 2
+
+#define EVT_END_CH 'c'
+#define EVT_CH 'e'
+
+static struct {
+ pthread_t file_svc_thid;
+ pthread_mutex_t file_svc_lock;
+ int ctrl_pipe[PIPE_MAX];
+ int evt_pipe[PIPE_MAX];
+ struct dlist *request_list;
+ int file_service_fd;
+} s_info = {
+ .ctrl_pipe = { -1, -1 },
+ .evt_pipe = { -1, -1 },
+ .request_list = NULL,
+ .file_service_fd = -1,
+};
+
+struct request_item {
+ char *filename;
+ char *save_to;
+ void (*result_cb)(const char *filename, const char *save_to, int ret, void *data);
+ void *data;
+ int ret;
+};
+
+/*!
+ * File transfer header.
+ * This must should be shared with client.
+ */
+struct burst_head {
+ off_t size;
+ int flen;
+ char fname[];
+};
+
+struct burst_data {
+ int size;
+ char data[];
+};
+
+static inline int put_event_ch(int fd, char ch)
+{
+ int ret;
+
+ ret = write(fd, &ch, sizeof(ch));
+ if (ret != sizeof(ch)) {
+ ErrPrint("write: %s\n", strerror(errno));
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int get_event_ch(int fd)
+{
+ int ret;
+ char ch;
+
+ ret = read(fd, &ch, sizeof(ch));
+ if (ret != sizeof(ch)) {
+ ErrPrint("read: %s\n", strerror(errno));
+ return ret;
+ }
+
+ ret = (int)((unsigned int)ch);
+ return ret;
+}
+
+static inline int file_service_close(int fd)
+{
+ return secure_socket_destroy_handle(fd);
+}
+
+static inline int file_service_open(void)
+{
+ char *addr;
+ int port;
+ char *file_addr;
+ int len;
+ int fd;
+
+ addr = malloc(strlen(client_addr()) + 1);
+ if (!addr) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ return -ENOMEM;
+ }
+
+ if (sscanf(client_addr(), COM_CORE_REMOTE_SCHEME"%[^:]:%d", addr, &port) != 2) {
+ ErrPrint("Invalid URL\n");
+ free(addr);
+ return -EINVAL;
+ }
+
+ len = strlen(COM_CORE_REMOTE_SCHEME);
+ len+= strlen(addr);
+ len+= 6; /* Port length? */
+
+ file_addr = malloc(len);
+ if (!file_addr) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ free(addr);
+ return -ENOMEM;
+ }
+
+ snprintf(file_addr, len, COM_CORE_REMOTE_SCHEME"%s:%d", addr, FILE_SERVICE_PORT);
+ DbgPrint("File service: %s\n", file_addr);
+ fd = secure_socket_create_client(file_addr);
+ free(file_addr);
+ free(addr);
+
+ return fd;
+}
+
+/*!
+ * Service Thread
+ */
+static void write_item_to_pipe(struct request_item *item, int ret)
+{
+ item->ret = LB_STATUS_ERROR_FAULT;
+ if (write(s_info.evt_pipe[PIPE_WRITE], &item, sizeof(item)) != sizeof(item)) {
+ ErrPrint("write: %s\n", strerror(errno));
+ free(item->filename);
+ free(item->save_to);
+ free(item);
+ item = NULL;
+ }
+}
+
+/*!
+ * Service Thread
+ */
+static void *file_service_main(void *data)
+{
+ int ret = 0;
+ int select_fd;
+ struct timeval tv;
+ fd_set set;
+ int offset;
+ enum {
+ RECV_INIT,
+ RECV_HEADER,
+ RECV_DATA,
+ } recv_state;
+ struct burst_head *head;
+ struct burst_data *body;
+ int recvsz;
+ struct request_item *item;
+ int file_offset;
+ int file_fd;
+
+ head = NULL;
+ item = NULL;
+ recv_state = RECV_INIT;
+ select_fd = (s_info.file_service_fd > s_info.ctrl_pipe[PIPE_READ] ? s_info.file_service_fd : s_info.ctrl_pipe[PIPE_READ]) + 1;
+ while (ret == 0) {
+ FD_ZERO(&set);
+ FD_SET(s_info.file_service_fd, &set);
+ FD_SET(s_info.ctrl_pipe[PIPE_READ], &set);
+
+ tv.tv_sec = 3;
+ tv.tv_usec = 0;
+ ret = select(select_fd , &set, NULL, NULL, &tv);
+ if (ret < 0) {
+ ret = -errno;
+ if (errno == EINTR) {
+ ErrPrint("INTERRUPTED\n");
+ ret = 0;
+ continue;
+ }
+ ErrPrint("Error: %s\n", strerror(errno));
+ break;
+ } else if (ret == 0) {
+ ErrPrint("Timeout\n");
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ if (item && FD_ISSET(s_info.file_service_fd, &set)) {
+ switch (recv_state) {
+ case RECV_INIT:
+ if (head == NULL) {
+ recvsz = sizeof(*head);
+
+ head = malloc(recvsz);
+ if (!head) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ ret = LB_STATUS_ERROR_MEMORY;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ break;
+ }
+
+ offset = 0;
+ recv_state = RECV_HEADER;
+ }
+ case RECV_HEADER:
+ if (offset < recvsz) {
+ ret = secure_socket_recv(s_info.file_service_fd, (char *)head + offset, recvsz - offset, NULL);
+ if (ret > 0) {
+ offset += ret;
+ } else {
+ free(head);
+ head = NULL;
+ recv_state = RECV_INIT;
+ ret = LB_STATUS_ERROR_FAULT;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ break;
+ }
+ }
+
+ if (offset == sizeof(*head)) {
+ void *tmp;
+
+ recvsz += head->flen;
+
+ tmp = realloc(head, recvsz);
+ if (!tmp) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+
+ free(head);
+ head = NULL;
+ recv_state = RECV_INIT;
+
+ ret = LB_STATUS_ERROR_MEMORY;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ break;
+ }
+
+ head = tmp;
+ } else if (offset == recvsz) {
+ DbgPrint("Filesize: %d, name[%s]\n", head->size, head->fname);
+ if (strcmp(item->filename, head->fname)) {
+ ErrPrint("Invalid data sequence (%s <> %s)\n", item->filename, head->fname);
+
+ free(head);
+ head = NULL;
+ recv_state = RECV_INIT;
+ ret = LB_STATUS_ERROR_FAULT;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ break;
+ }
+
+ file_fd = open(item->save_to, O_WRONLY|O_CREAT, 0644);
+ if (file_fd < 0) {
+ ErrPrint("open: %s\n", strerror(errno));
+ free(head);
+ head = NULL;
+ recv_state = RECV_INIT;
+
+ ret = LB_STATUS_ERROR_IO;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ break;
+ }
+
+ recv_state = RECV_DATA;
+ body = NULL;
+
+ } else {
+ ErrPrint("Invalid state\n");
+ free(head);
+ head = NULL;
+ recv_state = RECV_INIT;
+ ret = LB_STATUS_ERROR_INVALID;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ }
+ break;
+ case RECV_DATA:
+ if (!body) {
+ body = malloc(sizeof(*body));
+ if (!body) {
+ free(head);
+ head = NULL;
+ recv_state = RECV_INIT;
+ ret = LB_STATUS_ERROR_MEMORY;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ break;
+ }
+
+ recvsz = sizeof(*body);
+ offset = 0;
+ }
+
+ ret = secure_socket_recv(s_info.file_service_fd, (char *)body + offset, recvsz - offset, NULL);
+ if (ret > 0) {
+ offset += ret;
+ } else {
+ free(head);
+ head = NULL;
+ free(body);
+ body = NULL;
+ recv_state = RECV_INIT;
+ ret = LB_STATUS_ERROR_FAULT;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ break;
+ }
+
+ if (offset == sizeof(*body)) {
+ void *tmp;
+
+ if (body->size < 0) {
+ ErrPrint("body->size: %d\n", body->size);
+ free(head);
+ head = NULL;
+ free(body);
+ body = NULL;
+ recv_state = RECV_INIT;
+ ret = LB_STATUS_ERROR_FAULT;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ break;
+ }
+
+ recvsz += body->size;
+
+ tmp = realloc(body, recvsz);
+ if (!tmp) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ free(head);
+ head = NULL;
+
+ free(body);
+ body = NULL;
+ recv_state = RECV_INIT;
+
+ ret = LB_STATUS_ERROR_MEMORY;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ break;
+ }
+ } else if (offset == recvsz) {
+ /* Flush this to the file */
+ ret = write(file_fd, body->data, body->size);
+ if (ret < 0) {
+ ErrPrint("write: %s\n", strerror(errno));
+ free(head);
+ head = NULL;
+
+ free(body);
+ body = NULL;
+ recv_state = RECV_INIT;
+
+ ret = LB_STATUS_ERROR_IO;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ break;
+ } else {
+ if (body->size != ret) {
+ DbgPrint("Body is not flushed correctly: %d, %d\n", ret, body->size);
+ ret = body->size;
+ }
+
+ file_offset += ret;
+ if (file_offset == head->size) {
+ if (close(file_fd) < 0) {
+ ErrPrint("close: %s\n", strerror(errno));
+ }
+ ret = LB_STATUS_SUCCESS;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ }
+ }
+
+ free(body);
+ body = NULL;
+
+ free(head);
+ head = NULL;
+
+ recv_state = RECV_INIT;
+ } else {
+ ErrPrint("Invalid state\n");
+
+ ret = -EFAULT;
+ free(body);
+ body = NULL;
+ free(head);
+ head = NULL;
+ recv_state = RECV_INIT;
+
+ ret = LB_STATUS_ERROR_FAULT;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ }
+ break;
+ default:
+ ErrPrint("Unknown event: %d\n", recv_state);
+ ret = LB_STATUS_ERROR_FAULT;
+ write_item_to_pipe(item, ret);
+ item = NULL;
+ break;
+ }
+ } else if (item == NULL && recv_state == RECV_INIT && FD_ISSET(s_info.ctrl_pipe[PIPE_READ], &set)) {
+ int ch;
+ struct dlist *l;
+
+ /* Only if the recv state is not changed, we can get next request item */
+ ch = get_event_ch(s_info.ctrl_pipe[PIPE_READ]);
+ if (ch == EVT_END_CH) {
+ DbgPrint("Service thread is canceled\n");
+ break;
+ }
+
+ CRITICAL_SECTION_BEGIN(&s_info.file_svc_lock);
+ l = dlist_nth(s_info.request_list, 0);
+ item = dlist_data(l);
+ s_info.request_list = dlist_remove(s_info.request_list, l);
+ CRITICAL_SECTION_END(&s_info.file_svc_lock);
+ }
+ }
+
+ return (void *)ret;
+}
+
+/* Master */
+static gboolean evt_cb(GIOChannel *src, GIOCondition cond, gpointer data)
+{
+ int fd;
+ struct request_item *item;
+
+ fd = g_io_channel_unix_get_fd(src);
+
+ if (!(cond & G_IO_IN)) {
+ DbgPrint("Client is disconencted\n");
+ return FALSE;
+ }
+
+ if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) {
+ DbgPrint("Client connection is lost\n");
+ return FALSE;
+ }
+
+ if (read(fd, &item, sizeof(item)) != sizeof(item)) {
+ ErrPrint("read: %s\n", strerror(errno));
+ } else {
+ if (item->result_cb) {
+ item->result_cb(item->filename, item->save_to, item->ret, item->data);
+ }
+
+ free(item->filename);
+ free(item->save_to);
+ free(item);
+ }
+
+ return TRUE;
+}
+
+int file_service_send_request(const char *filename, const char *save_to, void (*result_cb)(const char *filename, const char *save_to, int ret, void *data), void *data)
+{
+ struct request_item *item;
+
+ item = malloc(sizeof(*item));
+ if (!item) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ return -ENOMEM;
+ }
+
+ item->filename = strdup(filename);
+ if (!item->filename) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ free(item);
+ return -ENOMEM;
+ }
+
+ item->save_to = strdup(save_to);
+ if (!item->save_to) {
+ ErrPrint("Heap: %s\n", strerror(errno));
+ free(item->filename);
+ free(item);
+ return -ENOMEM;
+ }
+
+ item->result_cb = result_cb;
+ item->data = data;
+
+ CRITICAL_SECTION_BEGIN(&s_info.file_svc_lock);
+ s_info.request_list = dlist_append(s_info.request_list, item);
+ CRITICAL_SECTION_END(&s_info.file_svc_lock);
+ return 0;
+}
+
+int file_service_init(void)
+{
+ int status;
+ GIOChannel *gio;
+ guint id;
+
+ if (strncmp(client_addr(), COM_CORE_REMOTE_SCHEME, strlen(COM_CORE_REMOTE_SCHEME))) {
+ return 0;
+ }
+
+ s_info.file_service_fd = file_service_open();
+ if (s_info.file_service_fd < 0) {
+ return -EFAULT;
+ }
+
+ if (pipe2(s_info.ctrl_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
+ ErrPrint("file service: %s\n", strerror(errno));
+ file_service_close(s_info.file_service_fd);
+ s_info.file_service_fd = -1;
+ return -EFAULT;
+ }
+
+ if (pipe2(s_info.evt_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
+ ErrPrint("file service: %s\n", strerror(errno));
+ CLOSE_PIPE(s_info.ctrl_pipe);
+ file_service_close(s_info.file_service_fd);
+ s_info.file_service_fd = -1;
+ return -EFAULT;
+ }
+
+ status = pthread_mutex_init(&s_info.file_svc_lock, NULL);
+ if (status != 0) {
+ ErrPrint("Mutex: %s\n", strerror(status));
+ CLOSE_PIPE(s_info.ctrl_pipe);
+ CLOSE_PIPE(s_info.evt_pipe);
+ file_service_close(s_info.file_service_fd);
+ s_info.file_service_fd = -1;
+ return -EFAULT;
+ }
+
+ gio = g_io_channel_unix_new(s_info.evt_pipe[PIPE_READ]);
+ if (!gio) {
+ ErrPrint("io channel new\n");
+ status = pthread_mutex_destroy(&s_info.file_svc_lock);
+ if (status != 0) {
+ ErrPrint("destroy: %s\n", strerror(status));
+ }
+ CLOSE_PIPE(s_info.ctrl_pipe);
+ CLOSE_PIPE(s_info.evt_pipe);
+ file_service_close(s_info.file_service_fd);
+ s_info.file_service_fd = -1;
+ return -EFAULT;
+ }
+
+ g_io_channel_set_close_on_unref(gio, FALSE);
+
+ id = g_io_add_watch(gio, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)evt_cb, NULL);
+ if (id <= 0) {
+ GError *err = NULL;
+ ErrPrint("Failed to add IO watch\n");
+ g_io_channel_shutdown(gio, TRUE, &err);
+ if (err) {
+ ErrPrint("Shutdown: %s\n", err->message);
+ g_error_free(err);
+ }
+ g_io_channel_unref(gio);
+
+ status = pthread_mutex_destroy(&s_info.file_svc_lock);
+ if (status != 0) {
+ ErrPrint("destroy: %s\n", strerror(status));
+ }
+ CLOSE_PIPE(s_info.ctrl_pipe);
+ CLOSE_PIPE(s_info.evt_pipe);
+ file_service_close(s_info.file_service_fd);
+ s_info.file_service_fd = -1;
+ return -EIO;
+ }
+
+ status = pthread_create(&s_info.file_svc_thid, NULL, file_service_main, NULL);
+ if (status != 0) {
+ GError *err = NULL;
+ ErrPrint("Failed to add IO watch\n");
+ g_io_channel_shutdown(gio, TRUE, &err);
+ if (err) {
+ ErrPrint("Shutdown: %s\n", err->message);
+ g_error_free(err);
+ }
+ g_io_channel_unref(gio);
+
+ ErrPrint("file service: %s\n", strerror(status));
+ CLOSE_PIPE(s_info.ctrl_pipe);
+ CLOSE_PIPE(s_info.evt_pipe);
+ file_service_close(s_info.file_service_fd);
+ s_info.file_service_fd = -1;
+
+ status = pthread_mutex_destroy(&s_info.file_svc_lock);
+ if (status != 0) {
+ ErrPrint("destroy: %s\n", strerror(status));
+ }
+
+ return -EFAULT;
+ }
+
+ g_io_channel_unref(gio);
+ return 0;
+}
+
+int file_service_fini(void)
+{
+ void *svc_ret;
+ int ret;
+
+ if (strncmp(client_addr(), COM_CORE_REMOTE_SCHEME, strlen(COM_CORE_REMOTE_SCHEME))) {
+ return 0;
+ }
+
+ (void)put_event_ch(s_info.ctrl_pipe[PIPE_WRITE], EVT_END_CH);
+
+ ret = pthread_join(s_info.file_svc_thid, &svc_ret);
+ if (ret != 0) {
+ ErrPrint("join: %s\n", strerror(ret));
+ } else {
+ DbgPrint("file svc returns: %d\n", (int)svc_ret);
+ }
+
+ ret = pthread_mutex_destroy(&s_info.file_svc_lock);
+ if (ret != 0) {
+ ErrPrint("destroy: %s\n", strerror(ret));
+ }
+
+ CLOSE_PIPE(s_info.evt_pipe);
+ CLOSE_PIPE(s_info.ctrl_pipe);
+
+ return 0;
+}
+
+/* End of a file */