return UPDATE_CONTROL_ERROR_NONE;
}
-static void do_update_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
+API int update_control_do_update(void)
{
- if (proxy == NULL) {
- _E("Not initialized");
- return;
- }
+ CHECK_FEATURE_SUPPORTED(DEVICE_UPDATE_FEATURE);
+ _I("update_control_do_update called");
gint status = 0;
GError *error = NULL;
- org_tizen_update_manager_call_install_finish(proxy, &status, res, &error);
+ if (proxy == NULL) {
+ _E("Not initialized");
+ return UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
+ }
+
+ org_tizen_update_manager_call_install_sync(proxy, &status, NULL, &error);
if (error != NULL) {
- _E("Failed to method call <install> to dbus : %s", error->message);
+ _E("Failed to call DBus <install> method: %s", error->message);
g_error_free(error);
- return;
+ return UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
}
- _I("Success to method call <install> to dbus");
+ _I("Successfully called DBus <install> method call");
if (status < 0) {
- _E("Failed to do update : %d", status);
- return;
- }
-
- // NB: We shouldn't really end up here, since the target should be rebooted by now.
- _I("Success to do update : %d", status);
-}
-
-API int update_control_do_update(void)
-{
- CHECK_FEATURE_SUPPORTED(DEVICE_UPDATE_FEATURE);
- _I("update_control_do_update called");
-
- if (proxy == NULL) {
- _E("Not initialized");
- return UPDATE_CONTROL_ERROR_SYSTEM_ERROR;
+ _E("DBus method <install> returned error code: %d", status);
+ return UPDATE_CONTROL_ERROR_INVALID_OPERATION;
}
- // TODO: Take the fact that this is now async into account in docs.
- // TODO: Consider making this asynchronous server-side instead.
- org_tizen_update_manager_call_install(proxy, NULL, do_update_cb, NULL);
- _I("Started update");
+ _I("Started update: %d", status);
return UPDATE_CONTROL_ERROR_NONE;
}
}
}
+ /* All basic checks succeeded, following is what happens next: We
+ * double-fork() the process and run actual upgrade script in the child
+ * process. We do this so that we can at least inform API client that
+ * script executed sucessfuly, without requiring him to wait for whole
+ * upgrade to succeed till returning from dbus call.
+ * TODO: Take the fact that this is now async into account in docs. */
pid = fork();
if (pid < 0) {
_FLOGE("Failed to fork");
status = -1;
goto execute_destroy;
} else if (pid == 0) {
+ /* Double fork to avoid zombies */
+ switch (fork()) {
+ case -1:
+ exit(EXIT_FAILURE);
+ case 0:
+ break;
+ default:
+ exit(EXIT_SUCCESS);
+ }
+
ret = execl(FOTA_TRIGGER_PATH, FOTA_TRIGGER_PATH, client_delta_path, NULL);
if (ret == -1)
_FLOGE("Failed to execl(%s %s)", FOTA_TRIGGER_PATH, client_delta_path);
exit(EXIT_FAILURE);
}
- ret = wait(&exec_status);
+ /* Since we double forked, we have to wait for the first child (otherwise
+ * it will become a zombie), but it should return quickly. */
+ ret = waitpid(pid, &exec_status, 0);
if (ret == -1) {
_FLOGE("Failed to wait child");
status = -1;
goto execute_destroy;
}
- if (WIFEXITED(exec_status) == TRUE) {
- exec_status = WEXITSTATUS(exec_status);
- if (exec_status != 100) {
- _FLOGE("Trigger fota failed, exit code(%d)", exec_status);
- status = -4;
- goto execute_destroy;
- }
- } else if (WIFSIGNALED(exec_status) == TRUE) {
- _FLOGE("Trigger fota killed, signal(%d)", WTERMSIG(exec_status));
- status = -4;
- goto execute_destroy;
- } else if (WIFSTOPPED(exec_status) == TRUE) {
- _FLOGE("Trigger fota stoped, signal(%d)", WSTOPSIG(exec_status));
- status = -4;
- goto execute_destroy;
- } else {
- _FLOGE("Unexpected wait error");
- status = -1;
- goto execute_destroy;
- }
-
- ret = device_power_reboot(FOTA_INSTALL_REBOOT_REASON);
- if (ret != DEVICE_ERROR_NONE) {
- _FLOGE("Failed to reboot with %s", FOTA_INSTALL_REBOOT_REASON);
- status = -1;
- goto execute_destroy;
- }
-
- _FLOGI("Success to trigger fota, device will be rebooted");
-
execute_destroy:
util_file_remove(FOTA_DELTA_BUILD_STRING_PATH);