Add on-demand with socket activation 08/306808/16
authorJiyong <jiyong.min@samsung.com>
Wed, 28 Feb 2024 00:39:56 +0000 (09:39 +0900)
committerJiyong <jiyong.min@samsung.com>
Sun, 10 Mar 2024 23:35:33 +0000 (08:35 +0900)
- The media-server runs in 3 cases
  booting
  socket activation by application
  udev rules by external storage mount/unmount

- The media-server must save external storage list for checking unmount

- Auto-scan with path activation is disabled except booting

Change-Id: I8f3d3d95f23a1cb4429c7bf0707a58e9683539ce

18 files changed:
Makefile.am
configure.ac
lib/include/media-util-dbg.h
lib/include/media-util-ipc.h
lib/include/media-util-socket-activation.h [new file with mode: 0644]
lib/include/media-util.h
lib/media-util-db.c
lib/media-util-register.c
lib/media-util-socket-activation.c [new file with mode: 0644]
packaging/99-media-server.rules [new file with mode: 0644]
packaging/media-server-user-ondemand.path [new file with mode: 0644]
packaging/media-server.socket [new file with mode: 0644]
packaging/media-server.spec
src/server/include/media-server-on-demand.h [new file with mode: 0644]
src/server/media-server-device-block.c
src/server/media-server-main.c
src/server/media-server-on-demand.c [new file with mode: 0644]
src/server/media-server-socket.c

index ebe1530..4701cb8 100644 (file)
@@ -59,6 +59,9 @@ libmedia_utils_la_SOURCES = lib/media-util-noti.c \
                             lib/media-util-register.c \
                             lib/media-util-cynara.c \
                             lib/media-util-user.c
+if USE_ON_DEMAND
+libmedia_utils_la_SOURCES += lib/media-util-socket-activation.c
+endif
 
 libmedia_utils_la_CFLAGS = -I${srcdir}/lib/include \
                            -D_FILE_OFFSET_BITS=64 \
@@ -112,6 +115,10 @@ media_server_SOURCES += src/common/media-common-utils-tv.c \
                       src/server/media-server-db-manage.c
 endif
 
+if USE_ON_DEMAND
+media_server_SOURCES += src/server/media-server-on-demand.c
+endif
+
 media_server_CFLAGS = -I${srcdir}/src/server/include \
                        $(SQLITE3_CFLAGS) \
                        $(COMMON_CFLAGS) \
@@ -225,3 +232,7 @@ includeheaders_HEADERS = lib/include/media-util-noti.h \
                          lib/include/media-util-cynara.h \
                          lib/include/media-util.h \
                          lib/include/media-util-user.h
+
+if USE_ON_DEMAND
+includeheaders_HEADERS += lib/include/media-util-socket-activation.h
+endif
index aec33b8..ca56a70 100644 (file)
@@ -147,6 +147,18 @@ if test "x$USE_PRODUCT_TV" = "xyes"; then
 fi
 AM_CONDITIONAL(USE_PRODUCT_TV, test "x$USE_PRODUCT_TV" = "xyes")
 
+# for on-demand
+AC_ARG_ENABLE(on_demand, AC_HELP_STRING([--enable-on-demand], [on-demand server]),
+        [
+               case "${enableval}" in
+               yes) USE_ON_DEMAND=yes ;;
+               no)  USE_ON_DEMAND=no ;;
+                 *)   AC_MSG_ERROR(bad value ${enableval} for --enable-on-demand) ;;
+               esac
+       ],
+[USE_ON_DEMAND=no])
+AM_CONDITIONAL(USE_ON_DEMAND, test "x$USE_ON_DEMAND" = "xyes")
+
 # for gtests
 AC_ARG_ENABLE(tests, AC_HELP_STRING([--enable-tests], [unittest build]),
         [
index fa43cca..4e5467d 100644 (file)
                        LOGD(FONT_COLOR_RESET);     \
                } while (0)
 
+#define MSAPI_DBG_FENTER() do { \
+                       LOGD(FONT_COLOR_YELLOW"<ENTER>"FONT_COLOR_RESET);     \
+               } while (0)
+
+#define MSAPI_DBG_FLEAVE() do { \
+                       LOGD(FONT_COLOR_YELLOW"<LEAVE>"FONT_COLOR_RESET);     \
+               } while (0)
+
 #define MSAPI_RETV_IF(expr, val) do { \
                        if (expr) { \
                                LOGE(FONT_COLOR_RED""FONT_COLOR_RESET);   \
index 0dbe44c..a53bb3a 100644 (file)
@@ -54,6 +54,9 @@ typedef enum {
        MS_MSG_RECURSIVE_START,
        MS_MSG_MEDIA_DB_RESET,  /**< media DB is reset*/
        MS_MSG_MEDIA_DB_MALFORMED,
+#ifdef _USE_ON_DEMAND
+       MS_MSG_MEDIA_SERVER_READY,
+#endif
        MS_MSG_MAX                                                      /**< Invalid msg type */
 } ms_msg_type_e;
 
diff --git a/lib/include/media-util-socket-activation.h b/lib/include/media-util-socket-activation.h
new file mode 100644 (file)
index 0000000..d7aac0b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Media Utility
+ *
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ *
+ */
+
+#ifndef _MEDIA_UTIL_SOCKET_ACTIVATION_H_
+#define _MEDIA_UTIL_SOCKET_ACTIVATION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ms_ipc_activate_media_server(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_MEDIA_UTIL_SOCKET_ACTIVATION_H_*/
index ad4c964..89f463d 100644 (file)
@@ -27,5 +27,8 @@
 #include <media-util-ipc.h>
 #include <media-util-cynara.h>
 #include <media-util-user.h>
+#ifdef _USE_ON_DEMAND
+#include <media-util-socket-activation.h>
+#endif
 
 #endif /*_MEDIA_UTIL_H_*/
index 6e8bab8..f24995d 100644 (file)
@@ -31,6 +31,9 @@
 #include "media-util-db.h"
 #include "media-util-err.h"
 #include "media-util-user.h"
+#ifdef _USE_ON_DEMAND
+#include "media-util-socket-activation.h"
+#endif
 
 static int __media_db_busy_handler(void *pData, int count)
 {
@@ -295,7 +298,15 @@ int media_db_request_update_db(const char *query_str, uid_t uid)
 
        MSAPI_RETVM_IF(!query_str || strlen(query_str) == 0, MS_MEDIA_ERR_INVALID_PARAMETER, "Invalid Query");
 
+#ifdef _USE_ON_DEMAND
+       ret = ms_ipc_activate_media_server();
+       if (ret != MS_MEDIA_ERR_NONE) {
+               MSAPI_DBG_ERR("ms_ipc_activate_media_server failed : %d", ret);
+               return ret;
+       }
+#endif
        ret = ms_ipc_request_update_tcp(MS_MSG_DB_UPDATE, query_str, uid);
+
        if (ret != MS_MEDIA_ERR_NONE)
                MSAPI_DBG_ERR("ms_ipc_request_update_tcp failed : %d", ret);
 
index dd731bd..98631f5 100644 (file)
@@ -26,6 +26,9 @@
 #include "media-util-err.h"
 #include "media-util-user.h"
 #include "media-util-ipc.h"
+#ifdef _USE_ON_DEMAND
+#include "media-util-socket-activation.h"
+#endif
 
 static GMutex scan_req_mutex;
 
@@ -233,6 +236,14 @@ static int __media_db_request_update_async(ms_msg_type_e msg_type,
 
        MSAPI_RETVM_IF(!path || strlen(path) == 0, MS_MEDIA_ERR_INVALID_PARAMETER, "Invalid path");
 
+#ifdef _USE_ON_DEMAND
+       ret = ms_ipc_activate_media_server();
+       if (ret != MS_MEDIA_ERR_NONE) {
+               MSAPI_DBG_ERR("ms_ipc_activate_media_server failed : %d", ret);
+               return ret;
+       }
+#endif
+
        request_path = g_canonicalize_filename(path, NULL);
        MSAPI_DBG_SLOG("trimmed path[%s]", request_path);
 
@@ -261,7 +272,7 @@ static int __media_db_request_update_async(ms_msg_type_e msg_type,
 
        __attach_callback(request_path, &sockfd, user_callback, user_data);
 
-       return ret;
+       return MS_MEDIA_ERR_NONE;
 }
 
 static int __media_db_request_update_cancel(ms_msg_type_e msg_type, const char *path)
diff --git a/lib/media-util-socket-activation.c b/lib/media-util-socket-activation.c
new file mode 100644 (file)
index 0000000..46cb676
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Media Utility
+ *
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ *
+ */
+
+#include <glib.h>
+#include <unistd.h>
+#include <tzplatform_config.h>
+
+#include "media-util.h"
+#include "media-util-dbg.h"
+#include "media-util-err.h"
+#include "media-util-ipc.h"
+
+#define MS_SOCKET_ACTIVATION_PATH              tzplatform_mkpath(TZ_SYS_RUN, ".media-server.sock")
+#define MS_SOCKET_ACTIVATION_MSG               "MS_MSG_MEDIA_SERVER_READY"
+#define MAX_RECIEVE_RETRY_COUNT 3
+#define MAX_WAIT_DAEMON_COUNT 100
+
+static int __check_media_server()
+{
+       int ret = MS_MEDIA_ERR_NONE;
+       int sockfd = -1;
+       ms_comm_msg_s send_msg;
+       ms_comm_msg_s recv_msg;
+       int recv_msg_size = -1;
+       int retry_count = 0;
+
+       MSAPI_DBG_FENTER();
+
+       memset((void *)&send_msg, 0, sizeof(ms_comm_msg_s));
+       memset((void *)&recv_msg, 0, sizeof(ms_comm_msg_s));
+
+       send_msg.msg_type = MS_MSG_MEDIA_SERVER_READY;
+       g_strlcpy(send_msg.msg, MS_SOCKET_ACTIVATION_MSG, sizeof(send_msg.msg));
+
+       /*Create Socket*/
+       ret = ms_ipc_create_client_socket(0, &sockfd);
+       if (ret != MS_MEDIA_ERR_NONE) {
+               MSAPI_DBG_ERR("ms_ipc_create_client_socket failed : %d", ret);
+               return ret;
+       }
+
+       ret = ms_ipc_send_msg_to_server_tcp(sockfd, MS_SCANNER_PORT, &send_msg, NULL);
+       if (ret != MS_MEDIA_ERR_NONE) {
+               MSAPI_DBG_ERR("ms_ipc_send_msg_to_server failed : %d", ret);
+               close(sockfd);
+               return ret;
+       }
+
+       while ((recv_msg_size = recv(sockfd, &recv_msg, sizeof(recv_msg), 0)) < 0) {
+               if (errno == EINTR) {
+                       MSAPI_DBG_STRERROR("catch interrupt");
+                       continue;
+               } else if (errno == EWOULDBLOCK) {
+                       if (retry_count++ < MAX_RECIEVE_RETRY_COUNT) {
+                               MSAPI_DBG_ERR("[No-Error] Time Out. retry_count [%d]", retry_count);
+                               continue;
+                       }
+                       MSAPI_DBG_ERR("Timeout. Can't try any more");
+
+               } else {
+                       MSAPI_DBG_STRERROR("recv failed");
+               }
+               ret = MS_MEDIA_ERR_IPC;
+       }
+
+       if (ret == MS_MEDIA_ERR_NONE)
+               MSAPI_DBG("media_server return %d", recv_msg.result);
+
+       close(sockfd);
+       MSAPI_DBG_FLEAVE();
+       return ret;
+}
+
+static int __launch_media_server()
+{
+       int ret = MS_MEDIA_ERR_NONE;
+       int sockfd = -1;
+       struct sockaddr_un addr;
+
+       MSAPI_DBG_FENTER();
+
+       /*Create Socket*/
+       ret = ms_ipc_create_client_socket(0, &sockfd);
+       if (ret != MS_MEDIA_ERR_NONE) {
+               MSAPI_DBG_ERR("ms_ipc_create_client_socket failed : %d", ret);
+               return ret;
+       }
+
+       /* Set server Address */
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       g_strlcpy(addr.sun_path, MS_SOCKET_ACTIVATION_PATH, sizeof(addr.sun_path));
+
+       MSAPI_DBG_ERR("addr.sun_path. [%s]", addr.sun_path);
+
+       /* Connecting to the media db server */
+       if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+               MSAPI_DBG_STRERROR("connect error");
+               if (errno == EACCES || errno == EPERM)
+                       ret = MS_MEDIA_ERR_PERMISSION_DENIED;
+               else
+                       ret = MS_MEDIA_ERR_IPC;
+       }
+
+       close(sockfd);
+       MSAPI_DBG_FLEAVE();
+       return ret;
+}
+
+int ms_ipc_activate_media_server()
+{
+       int ret = MS_MEDIA_ERR_NONE;
+       int retry_count = 0;
+
+       MSAPI_DBG_FENTER();
+
+       /* check media-server is already executed */
+       if (__check_media_server() == MS_MEDIA_ERR_NONE)
+               return MS_MEDIA_ERR_NONE;
+
+       /* send activation to socket */
+       ret = __launch_media_server();
+       if (ret != MS_MEDIA_ERR_NONE) {
+               MSAPI_DBG_ERR("__launch_media_server failed : %d", ret);
+               return ret;
+       }
+
+       /* wait & verify that media-server has been executed successfully */
+       while (retry_count++ < MAX_WAIT_DAEMON_COUNT) {
+               MSAPI_DBG_ERR("[No-Error] retry_count [%d]", retry_count);
+               if (__check_media_server() == MS_MEDIA_ERR_NONE)
+                       break;
+               usleep(10000);
+       }
+
+       MSAPI_DBG_FLEAVE();
+       return ret;
+}
diff --git a/packaging/99-media-server.rules b/packaging/99-media-server.rules
new file mode 100644 (file)
index 0000000..2288457
--- /dev/null
@@ -0,0 +1 @@
+SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="filesystem", ENV{ID_BUS}=="usb", RUN+="/usr/bin/systemctl start media-server"
diff --git a/packaging/media-server-user-ondemand.path b/packaging/media-server-user-ondemand.path
new file mode 100644 (file)
index 0000000..d56dfa4
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=media-server-user path activation
+
+[Path]
+PathExists=/tmp/media_server_boot
diff --git a/packaging/media-server.socket b/packaging/media-server.socket
new file mode 100644 (file)
index 0000000..2c60f44
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=Media Server socket activation
+
+[Socket]
+SocketUser=multimedia_fw
+SocketGroup=multimedia_fw
+ListenStream=/run/.media-server.sock
+Service=media-server.service
+SocketMode=0777
+
+[Install]
+WantedBy=sockets.target
index c07e491..1adf489 100644 (file)
@@ -8,6 +8,9 @@ Source0:    %{name}-%{version}.tar.gz
 Source1:    media-server.service
 Source2:    media-server-user.service
 Source3:    media-server-user.path
+Source4:    media-server.socket
+Source5:    99-media-server.rules
+Source6:    media-server-user-ondemand.path
 Source1001:     %{name}.manifest
 Source1002:     libmedia-utils.manifest
 Source1003:     libmedia-utils-devel.manifest
@@ -37,6 +40,12 @@ BuildRequires:  pkgconfig(deviced)
 %define product_tv 0
 %endif
 
+%if ("%{sec_buildconf_optimized_memory}" == "1")
+%define on_demand 1
+%else
+%define on_demand 0
+%endif
+
 %define upgrade_script_path /usr/share/upgrade/scripts
 
 %description
@@ -67,13 +76,14 @@ cp %{SOURCE1001} %{SOURCE1002} %{SOURCE1003} .
 %if "%{asan}" == "1"
 %restore_fcommon
 %endif
-%if 0%{?product_tv}
-export CFLAGS="$CFLAGS -D_USE_TVPD_MODE"
-%else
-export CFLAGS="$CFLAGS"
-%endif
 export CFLAGS="$CFLAGS -D_GNU_SOURCE -D_FORTIFY_SOURCE=2 -DTIZEN_DEBUG_ENABLE -DSYSCONFDIR=\\\"%{_sysconfdir}\\\""
 export CFLAGS+=" -DPATH_LIBDIR=\\\"%{_libdir}\\\""
+%if 0%{?on_demand}
+export CFLAGS+=" -D_USE_ON_DEMAND"
+%endif
+%if 0%{?product_tv}
+export CFLAGS+=" -D_USE_TVPD_MODE"
+%endif
 rm -rf autom4te.cache
 rm -f aclocal.m4 ltmain.sh
 mkdir -p m4
@@ -81,6 +91,9 @@ mkdir -p m4
 %if 0%{?gtests:1}
        --enable-tests \
 %endif
+%if 0%{?on_demand}
+       --enable-on-demand \
+%endif
 %if 0%{?product_tv}
        --enable-product-tv \
 %else
@@ -95,11 +108,22 @@ mkdir -p m4
 
 mkdir -p %{buildroot}%{_unitdir}/multi-user.target.wants
 install -m 644 %{SOURCE1} %{buildroot}%{_unitdir}/media-server.service
+%if 0%{?on_demand}
+mkdir -p %{buildroot}%{_unitdir}/sockets.target.wants
+mkdir -p %{buildroot}%{_sysconfdir}/udev/rules.d
+install -m 644 %{SOURCE4} %{buildroot}%{_unitdir}/media-server.socket
+ln -s ../media-server.socket %{buildroot}%{_unitdir}/sockets.target.wants/media-server.socket
+install -m 644 %{SOURCE5} %{buildroot}%{_sysconfdir}/udev/rules.d/99-media-server.rules
+%endif
 %if !0%{?product_tv}
 mkdir -p %{buildroot}%{_unitdir_user}
 install -m 644 %{SOURCE2} %{buildroot}%{_unitdir_user}/media-server-user.service
+%if 0%{?on_demand}
+install -m 644 %{SOURCE6} %{buildroot}%{_unitdir_user}/media-server-user.path
+%else
 install -m 644 %{SOURCE3} %{buildroot}%{_unitdir_user}/media-server-user.path
 %endif
+%endif
 
 ln -s ../media-server.service %{buildroot}%{_unitdir}/multi-user.target.wants/media-server.service
 
@@ -143,6 +167,11 @@ chmod 755 /etc/gumd/useradd.d/30_media-server-add.post
 %endif
 %{_unitdir}/media-server.service
 %{_unitdir}/multi-user.target.wants/media-server.service
+%if 0%{?on_demand}
+%{_unitdir}/media-server.socket
+%{_unitdir}/sockets.target.wants/media-server.socket
+%{_sysconfdir}/udev/rules.d/99-media-server.rules
+%endif
 %if !0%{?product_tv}
 %{_unitdir_user}/media-server-user.service
 %{_unitdir_user}/media-server-user.path
diff --git a/src/server/include/media-server-on-demand.h b/src/server/include/media-server-on-demand.h
new file mode 100644 (file)
index 0000000..482d085
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Media Server
+ *
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ *
+ */
+
+#ifndef _MEDIA_SERVER_ON_DEMAND_H_
+#define _MEDIA_SERVER_ON_DEMAND_H_
+
+#include <glib.h>
+
+#include "media-common-system.h"
+
+typedef enum {
+    MS_START_TYPE_BOOT,
+    MS_START_TYPE_SOCKET,
+    MS_START_TYPE_UDEV,
+} ms_start_type_e;
+
+void ms_on_demand_start_timer(ms_start_type_e start_type);
+ms_start_type_e ms_on_demand_get_start_type(void);
+void ms_on_demand_initialize(void);
+void ms_on_demand_finalize(void);
+void ms_on_demand_insert_storage(gpointer data, gpointer user_data);
+void ms_on_demand_remove_storage(gpointer data, gpointer user_data);
+void ms_on_demand_update_storage_list(GSList *storage_list);
+
+#endif /* _MEDIA_SERVER_ON_DEMAND_H_ */
index 7598707..d333f82 100644 (file)
@@ -34,6 +34,9 @@
 #define PROD_CUSTOM_MIME_ADDED   "1"
 #define PROD_CUSTOM_MIME_REMOVED "0"
 #endif
+#ifdef _USE_ON_DEMAND
+#include "media-server-on-demand.h"
+#endif
 
 int ms_storage_insert_handler(const char *mount_path, const char *mount_uuid)
 {
@@ -411,10 +414,17 @@ void ms_device_block_changed_cb(ms_block_info_s *block_info, void *user_data)
        if (block_info->mount_path != NULL && block_info->mount_uuid != NULL) {
                MS_DBG("uuid[%s] path[%s]", block_info->mount_uuid, block_info->mount_path);
 
+#ifdef _USE_ON_DEMAND
+               if (block_info->state == MS_STG_INSERTED)
+                       ms_on_demand_insert_storage((gpointer)block_info, NULL);
+               else if (block_info->state == MS_STG_REMOVED)
+                       ms_on_demand_remove_storage((gpointer)block_info, NULL);
+#else
                if (block_info->state == MS_STG_INSERTED)
                        ms_storage_insert_handler(block_info->mount_path, block_info->mount_uuid);
                else if (block_info->state == MS_STG_REMOVED)
                        ms_storage_remove_handler(block_info->mount_path, block_info->mount_uuid);
+#endif
        } else {
                MS_DBG_ERR("mount_path or mount_uuid is NULL");
        }
index fd91cbd..eca6ebb 100644 (file)
 #include "media-server-scanner.h"
 #include "media-server-device-block.h"
 
+#ifdef _USE_ON_DEMAND
+#include "media-server-on-demand.h"
+#endif
+
 #ifdef _USE_TVPD_MODE
 #include "media-common-external-storage.h"
 #include "media-server-db-manage.h"
@@ -177,6 +181,9 @@ int main(int argc, char **argv)
 #ifdef _USE_TVPD_MODE
        int ret = 0;
 #endif
+#ifdef _USE_ON_DEMAND
+       ms_start_type_e start_type = ms_on_demand_get_start_type();
+#endif
 
        resource_pid_t stResource = {};
        stResource.pid = getpid();
@@ -200,7 +207,9 @@ int main(int argc, char **argv)
 
        __ms_deal_reset_status();
 #endif
-
+#ifdef _USE_ON_DEMAND
+       ms_on_demand_initialize();
+#endif
        /*Init main loop*/
        mainloop = g_main_loop_new(NULL, FALSE);
 
@@ -240,7 +249,25 @@ int main(int argc, char **argv)
        __ms_add_event_receiver();
        __ms_add_signal_handler();
 
+#ifdef _USE_ON_DEMAND
+       switch (start_type) {
+       case MS_START_TYPE_SOCKET:
+               /* do nothing */
+               break;
+       case MS_START_TYPE_UDEV:
+               /* improve mount/unmount performance */
+               MS_DBG("ms_scanner_start [%s]", (ms_scanner_start() == MS_MEDIA_ERR_NONE) ? "success" : "fail");
+               /* fall through */
+       case MS_START_TYPE_BOOT:
+               __ms_check_mediadb();
+               break;
+       default:
+               MS_DBG_ERR("invalid start_type(%d)", start_type);
+               break;
+       }
+#else
        __ms_check_mediadb();
+#endif
 
        /*Active flush */
        malloc_trim(0);
@@ -254,11 +281,18 @@ int main(int argc, char **argv)
 #endif
        resource_clear_cpu_boosting(stResource);
 
+#ifdef _USE_ON_DEMAND
+       ms_on_demand_start_timer(start_type);
+#endif
+
        g_main_loop_run(mainloop);
 
        ms_db_update_thread_join();
        ms_scanner_dispatcher_thread_join();
 
+#ifdef _USE_ON_DEMAND
+       ms_on_demand_finalize();
+#endif
        ms_cynara_finish();
 #ifdef _USE_TVPD_MODE
        ms_unload_functions();
@@ -311,11 +345,13 @@ static void __ms_dev_free(gpointer data)
        g_free(_data);
 }
 
+#ifndef _USE_ON_DEMAND
 static void __ms_insert_storage(gpointer data, gpointer user_data)
 {
        ms_block_info_s *block_info = (ms_block_info_s *)data;
        ms_storage_insert_handler(block_info->mount_path, block_info->mount_uuid);
 }
+#endif
 
 static void __ms_update_storage_status(void)
 {
@@ -328,6 +364,9 @@ static void __ms_update_storage_status(void)
                return;
        }
 
+#ifdef _USE_ON_DEMAND
+       ms_on_demand_update_storage_list(dev_list);
+#else
        if (!dev_list) {
                MS_DBG_ERR("External storage not found");
                ms_storage_remove_handler(STORAGE_REMOVED, NULL);
@@ -335,6 +374,7 @@ static void __ms_update_storage_status(void)
        }
 
        g_slist_foreach(dev_list, __ms_insert_storage, NULL);
+#endif
        g_slist_free_full(dev_list, __ms_dev_free);
 }
 
diff --git a/src/server/media-server-on-demand.c b/src/server/media-server-on-demand.c
new file mode 100644 (file)
index 0000000..fc385c3
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * Media Server
+ *
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ *
+ */
+
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <systemd/sd-daemon.h>
+#include <gio/gio.h>
+#include <tzplatform_config.h>
+#include <sqlite3.h>
+
+#include "media-common-db-svc.h"
+#include "media-util.h"
+
+#include "media-server-dbg.h"
+#include "media-server-db.h"
+#include "media-server-scanner.h"
+#include "media-server-device-block.h"
+#include "media-server-on-demand.h"
+
+#define MS_MAIN_TIMEOUT_SEC 300
+#define BOOT_CHECK_FILE tzplatform_mkpath(TZ_SYS_TMP, "media_server_boot")
+#define SELECT_VALID_STORAGES "SELECT storage_id, storage_path FROM storage WHERE validity=1"
+
+extern GMainLoop *mainloop;
+
+static int g_ms_on_demand_timer_id = 0;
+static GSList *g_ms_on_demand_storage_list = NULL;
+
+static void __ms_on_demand_create_timer(void);
+
+static gboolean __ms_on_demand_check_active(gpointer user_data)
+{
+       int scanner_pid = ms_get_scanner_pid();
+
+       /* check if scanner & dcm are running */
+       if (scanner_pid > 0) {
+               MS_DBG_ERR("[No-error] Timer is Called but there is active media-scanner, scanner_pid = %d", scanner_pid);
+
+               return G_SOURCE_CONTINUE;
+       }
+
+       MS_DBG_ERR("[No-error] Timer is Called. Now Killing media-server process");
+       g_ms_on_demand_timer_id = 0;
+
+       /* stop media-server */
+       ms_scanner_dispatcher_thread_stop();
+       ms_db_update_thread_stop();
+       g_main_loop_quit(mainloop);
+
+       return G_SOURCE_REMOVE;
+}
+
+static void __ms_on_demand_destroy_timer(void)
+{
+       if (g_source_remove(g_ms_on_demand_timer_id))
+               g_ms_on_demand_timer_id = 0;
+       MS_DBG_ERR("[No-error] __ms_on_demand_destroy_timer");
+}
+
+static void __ms_on_demand_create_timer(void)
+{
+       __ms_on_demand_destroy_timer();
+
+       MS_DBG_ERR("[No-error] __ms_on_demand_create_timer");
+
+       g_ms_on_demand_timer_id = g_timeout_add_seconds(MS_MAIN_TIMEOUT_SEC,
+               __ms_on_demand_check_active, NULL);
+}
+
+static bool __ms_on_demand_is_boot()
+{
+       g_autoptr (GFile) tmpfile = NULL;
+
+       tmpfile = g_file_new_for_path(BOOT_CHECK_FILE);
+
+       if (g_file_query_exists(tmpfile, NULL))
+               return false;
+
+       return true;
+}
+
+static void __ms_on_demand_write_boot_status(void)
+{
+       GError *error = NULL;
+
+       if (!g_file_set_contents(BOOT_CHECK_FILE, (const gchar *)"1", (gssize)1, &error)) {
+               MS_DBG_ERR("g_file_set_contents error(%s: %s)",
+                       BOOT_CHECK_FILE, (error ? error->message : "none"));
+               if (error)
+                       g_error_free(error);
+       }
+}
+
+static bool __ms_on_demand_acquire_socket_activation(void)
+{
+       int ret = 0;
+       int client_fd = -1;
+       struct sockaddr_in client_addr;
+       socklen_t client_addr_size = sizeof(client_addr);
+       fd_set readfds;
+       struct timeval  timeout = { 0, 10000 }; // 10 msec
+
+       MS_DBG("SD_LISTEN_FDS_START: %d", SD_LISTEN_FDS_START);
+
+       /* If the media-server is executed by udev, the block occur in accept() function.
+          So select() function is used to prevent block. */
+       FD_ZERO(&readfds);
+       FD_SET(SD_LISTEN_FDS_START, &readfds);
+       ret = select(SD_LISTEN_FDS_START + 1, &readfds, NULL, NULL, &timeout);
+       MS_DBG("select: %d", ret);
+       MS_DBG_RETVM_IF(ret < 1, false, "[No-error] not socket activation.");
+
+       /* It is necessary to accept fd so that no more socket activation occur from the systemd. */
+       client_fd = accept(SD_LISTEN_FDS_START, (struct sockaddr *)&client_addr, &client_addr_size);
+       if (client_fd == -1) {
+               MS_DBG_STRERROR("accept failed");
+               return false;
+       }
+       close(client_fd);
+       return true;
+}
+
+void ms_on_demand_start_timer(ms_start_type_e start_type)
+{
+       /* start kill timer */
+       __ms_on_demand_create_timer();
+       if (start_type == MS_START_TYPE_BOOT)
+               __ms_on_demand_write_boot_status();
+}
+
+ms_start_type_e ms_on_demand_get_start_type(void)
+{
+       /* Media-server is initially executed without temp file at boot time. */
+       bool is_boot = __ms_on_demand_is_boot();
+       MS_DBG_RETVM_IF(is_boot, MS_START_TYPE_BOOT, "[No-error] Media Server was started by boot.");
+
+       /* Media-server is executed with socket by socket activation. */
+       MS_DBG_RETVM_IF(__ms_on_demand_acquire_socket_activation(), MS_START_TYPE_SOCKET,
+               "[No-error] Media Server was started by socket activation.");
+
+       /* Media-server can be executed by udev except boot and socket activation */
+       MS_DBG_ERR("[No-error] Media Server was started by udev.");
+       return MS_START_TYPE_UDEV;
+}
+
+static void __ms_read_storage_list_from_db(void)
+{
+       int ret = MS_MEDIA_ERR_NONE;
+       sqlite3 *db_handle = NULL;
+       sqlite3_stmt *sql_stmt = NULL;
+       ms_block_info_s *block_info = NULL;
+
+       ret = ms_connect_db(&db_handle, ms_sys_get_uid());
+       if (ret != MS_MEDIA_ERR_NONE) {
+               MS_DBG_ERR("ms_connect_db failed [%d]", ret);
+               return;
+       }
+
+       ret = sqlite3_prepare_v2(db_handle, SELECT_VALID_STORAGES, strlen(SELECT_VALID_STORAGES), &sql_stmt, NULL);
+       if (SQLITE_OK != ret) {
+               MS_DBG_ERR("prepare error [%s]", sqlite3_errmsg(db_handle));
+               ms_disconnect_db(db_handle);
+               return;
+       }
+
+       while (sqlite3_step(sql_stmt) == SQLITE_ROW) {
+               block_info = g_new0(ms_block_info_s, 1);
+               block_info->mount_uuid = g_strdup((const char *)sqlite3_column_text(sql_stmt, 0));
+               block_info->mount_path = g_strdup((const char *)sqlite3_column_text(sql_stmt, 1));
+
+               g_ms_on_demand_storage_list = g_slist_append(g_ms_on_demand_storage_list, block_info);
+       }
+
+       sqlite3_finalize(sql_stmt);
+       ms_disconnect_db(db_handle);
+}
+
+static gint __ms_compare_storage(gconstpointer a, gconstpointer b)
+{
+       ms_block_info_s *_a = (ms_block_info_s *)a;
+       ms_block_info_s *_b = (ms_block_info_s *)b;
+
+       return g_strcmp0(_a->mount_path, _b->mount_path);
+}
+
+static void __ms_free_storage(gpointer data)
+{
+       ms_block_info_s *block_info = (ms_block_info_s *)data;
+
+       if (!block_info)
+               return;
+
+       g_free(block_info->mount_path);
+       g_free(block_info->mount_uuid);
+       g_free(block_info);
+}
+
+static void __ms_print_storage(gpointer data, gpointer user_data)
+{
+       ms_block_info_s *_data = (ms_block_info_s *)data;
+       const char *message = (const char *)user_data;
+
+       if (!data)
+               return;
+
+       MS_DBG_SINFO("[%s] Storage uuid[%s] path[%s]",
+               message ? message : "None", _data->mount_uuid, _data->mount_path);
+}
+
+/* diff = a - b, the diff exist in a, but it does not exist in b */
+static GSList *__ms_get_diff_list(GSList *a, GSList *b)
+{
+       GSList *diff = NULL;
+       GSList *iter = NULL;
+
+       for (iter = a; iter != NULL; iter = g_slist_next(iter)) {
+               if (!g_slist_find_custom(b, iter->data, (GCompareFunc)__ms_compare_storage))
+                       diff = g_slist_append(diff, iter->data);
+       }
+
+       return diff;
+}
+
+void ms_on_demand_initialize(void)
+{
+       MS_DBG_FENTER();
+
+       __ms_read_storage_list_from_db();
+       g_slist_foreach(g_ms_on_demand_storage_list, (GFunc)__ms_print_storage, "Initial");
+
+       MS_DBG_FLEAVE();
+}
+
+void ms_on_demand_finalize(void)
+{
+       g_slist_free_full(g_ms_on_demand_storage_list, (GDestroyNotify)__ms_free_storage);
+}
+
+void ms_on_demand_insert_storage(gpointer data, gpointer user_data)
+{
+       ms_block_info_s *block_info = (ms_block_info_s *)data;
+       ms_block_info_s *new_storage = NULL;
+
+       if (!block_info)
+               return;
+
+       if (g_slist_find_custom(g_ms_on_demand_storage_list, block_info,
+                       (GCompareFunc)__ms_compare_storage))
+               return;
+
+       __ms_print_storage(block_info, "Insert");
+
+       new_storage = g_new0(ms_block_info_s, 1);
+       new_storage->mount_path = g_strdup(block_info->mount_path);
+       new_storage->mount_uuid = g_strdup(block_info->mount_uuid);
+
+       g_ms_on_demand_storage_list = g_slist_append(g_ms_on_demand_storage_list, new_storage);
+
+       /* request database insertion to scanner */
+       ms_storage_insert_handler(block_info->mount_path, block_info->mount_uuid);
+}
+
+void ms_on_demand_remove_storage(gpointer data, gpointer user_data)
+{
+       ms_block_info_s *block_info = (ms_block_info_s *)data;
+       GSList *found = NULL;
+
+       if (!block_info)
+               return;
+
+       found = g_slist_find_custom(g_ms_on_demand_storage_list, block_info,
+                       (GCompareFunc)__ms_compare_storage);
+       if (!found)
+               return;
+
+       __ms_print_storage(block_info, "Remove");
+
+       /* request database removal to scanner */
+       ms_storage_remove_handler(block_info->mount_path, block_info->mount_uuid);
+
+       g_ms_on_demand_storage_list = g_slist_remove_link(g_ms_on_demand_storage_list, found);
+       g_slist_free_full(found, (GDestroyNotify)__ms_free_storage);
+}
+
+void ms_on_demand_update_storage_list(GSList *storage_list)
+{
+       GSList *insert_storages = NULL;
+       GSList *remove_storages = NULL;
+
+       MS_DBG_FENTER();
+
+       /* Insert the storages that don't exist in previous list(g_ms_on_demand_storage_list) */
+       insert_storages = __ms_get_diff_list(storage_list, g_ms_on_demand_storage_list);
+       if (insert_storages) {
+               g_slist_foreach(insert_storages, (GFunc)ms_on_demand_insert_storage, NULL);
+               g_slist_free(insert_storages);
+       }
+
+       /* Remove the storages that don't exist in new list(storage_list) */
+       remove_storages = __ms_get_diff_list(g_ms_on_demand_storage_list, storage_list);
+       if (remove_storages) {
+               g_slist_foreach(remove_storages, (GFunc)ms_on_demand_remove_storage, NULL);
+               g_slist_free(remove_storages);
+       }
+
+       MS_DBG_FLEAVE();
+}
index 46f6cac..e441d67 100644 (file)
@@ -243,6 +243,12 @@ gboolean ms_read_scanner_dispatcher_socket_func(GIOChannel *src, GIOCondition co
                /*media DB is corrupted, recovery media DB*/
                __ms_recovery_media_db(recv_msg.uid);
                goto ERROR;
+#ifdef _USE_ON_DEMAND
+       case MS_MSG_MEDIA_SERVER_READY:
+               ret = MS_MEDIA_ERR_NONE;
+               MS_DBG_INFO("%s", recv_msg.msg);
+               goto ERROR;
+#endif
        default:
                close(client_sock);
                break;
@@ -266,6 +272,12 @@ ERROR:
        case MS_MSG_MEDIA_DB_MALFORMED:
                res_msg.msg_type = MS_MSG_MEDIA_DB_MALFORMED;
                break;
+#ifdef _USE_ON_DEMAND
+       case MS_MSG_MEDIA_SERVER_READY:
+               res_msg.msg_type = MS_MSG_MEDIA_SERVER_READY;
+               res_msg.result = MS_MEDIA_ERR_NONE;
+               break;
+#endif
        default:
                break;
        }