process.c

Go to the documentation of this file.
00001 /*
00002  * File Name: process.c
00003  */
00004 
00005 /*
00006  * This file is part of sysd.
00007  *
00008  * sysd is free software: you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation, either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * sysd is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00020  */
00021 
00022 /**
00023  * Copyright (C) 2008 iRex Technologies B.V.
00024  * All rights reserved.
00025  */
00026 
00027 //----------------------------------------------------------------------------
00028 // Include Files
00029 //----------------------------------------------------------------------------
00030 
00031 // system include files, between < >
00032 #include <glib.h>
00033 #include <glib.h>
00034 #include <signal.h>
00035 #include <stdlib.h>
00036 #include <stdio.h>
00037 #include <string.h> 
00038 #include <unistd.h>
00039 
00040 #include <hal/libhal.h>
00041 
00042 // ereader include files, between < >
00043 #include <liberkeyb/erkeyb-client.h>
00044 
00045 // local include files, between " "
00046 #include "log.h"
00047 #include "busy.h"
00048 #include "conf.h"
00049 #include "connections.h"
00050 #include "display.h"
00051 #include "ipc.h"
00052 #include "hal.h"
00053 #include "process.h"
00054 #include "tasks.h"
00055 #include "xwindow.h"
00056 
00057 
00058 //----------------------------------------------------------------------------
00059 // Type Declarations
00060 //----------------------------------------------------------------------------
00061 
00062 
00063 //----------------------------------------------------------------------------
00064 // Constants
00065 //----------------------------------------------------------------------------
00066 
00067 static const gint TIMEOUT_STARTUP_COMPLETED = 30;
00068 
00069 
00070 //----------------------------------------------------------------------------
00071 // Static Variables
00072 //----------------------------------------------------------------------------
00073 
00074 static GSList   *g_proclist = NULL;     // element is proc_t*
00075 static gint     g_process_pending_source = 0;
00076 
00077 
00078 //============================================================================
00079 // Local Function Definitions
00080 //============================================================================
00081 
00082 static void     check_start_next(void);
00083 static void     on_process_exit(GPid pid, gint status, gpointer data);
00084 static gboolean on_startup_completed_timeout(gpointer data);
00085 static gboolean start_process(proc_t *proc);
00086 static void     check_respawn(proc_t *proc);
00087 static void     on_reply_error(eripc_context_t *context, const eripc_event_info_t *info, void *user_data);
00088 static void     post_process_startup(proc_t *proc, const char *application, gint window);
00089 static proc_t   *create_process(const char* command, const char* working_dir, GCallback startup_callback, GCallback exit_callback, gint flags);
00090 static void     destroy_process(proc_t *proc);
00091 
00092 
00093 //============================================================================
00094 // Functions Implementation
00095 //============================================================================
00096 
00097 gboolean process_add(const char* command, const char* working_dir, GCallback startup_callback, GCallback exit_callback, gint flags)
00098 {
00099     LOGPRINTF("entry: %s, %d", command, flags);
00100     
00101     proc_t *proc = create_process(command, working_dir, startup_callback, exit_callback, flags);
00102     if (proc == NULL)
00103     {
00104         return FALSE;
00105     }
00106     
00107     check_start_next();
00108     
00109     return TRUE;
00110 }
00111 
00112 
00113 proc_t *process_start(const char* command, const char* working_dir, GCallback startup_callback, GCallback exit_callback, gint flags)
00114 {
00115     LOGPRINTF("entry: %s, %d", command, flags);
00116     
00117     gboolean result = FALSE;
00118     
00119     proc_t *proc = create_process(command, working_dir, startup_callback, exit_callback, flags);
00120     if (proc == NULL)
00121     {
00122         return NULL;
00123     }
00124     
00125     result = start_process(proc);
00126     if (result == FALSE)
00127     {
00128         LOGPRINTF("failed to spawn, remove from list");
00129         destroy_process(proc);
00130         proc = NULL;
00131     }
00132     
00133     return proc;
00134 }
00135 
00136 
00137 void process_stop(proc_t *proc)
00138 {
00139     g_assert(proc);
00140     LOGPRINTF("entry: %s, %d", proc->command, proc->pid);
00141     
00142     if (proc && proc->pid > 0)
00143     {
00144         kill(proc->pid, SIGTERM);
00145     }
00146 }
00147 
00148 
00149 gboolean process_activate(const char* application)
00150 {
00151     LOGPRINTF("entry");
00152     
00153     gboolean result = FALSE;
00154     Window win_found;
00155     
00156     win_found = get_application_window(application);
00157     
00158     if (win_found != None)
00159     {
00160         window_activate(win_found);
00161         result = TRUE;
00162     }
00163     else
00164     {
00165         WARNPRINTF("No window found for: %s", application);
00166     }
00167 
00168     return result;
00169 }
00170 
00171 
00172 gboolean process_activate_ctb()
00173 {
00174     LOGPRINTF("entry");
00175     
00176     gboolean retval = TRUE;
00177 
00178     if (sys_get_device_state() == STATE_DEVICE_STOPPING)
00179     {
00180         // don't activate CTB when device is shutting down
00181         return FALSE;
00182     }
00183     
00184     // set to top when not already active
00185     gchar *app_active = display_get_active_window();
00186     if (app_active && strcmp(app_active, "ctb") != 0 )
00187     {
00188         retval = process_activate("ctb");
00189     }
00190     
00191     return retval;
00192 }
00193 
00194 
00195 gboolean process_startup_complete(const char *application, gint pid, gboolean is_multidoc, const char *ipc_service, gint window)
00196 {
00197     LOGPRINTF("entry");
00198     gboolean retval = FALSE;
00199     
00200     // find proc by pid
00201     proc_t *proc = process_get_by_pid(pid);
00202 
00203     if (proc == NULL)
00204     {
00205         WARNPRINTF("%s (pid %d) is not started sysd", application, pid);
00206     }
00207     else if (proc->state != STATE_STARTING)
00208     {
00209         WARNPRINTF("unexpected startupComplete received from %s (pid %d), state [%d]", application, pid, proc->state);
00210     }
00211     else
00212     {
00213         // stop pending timeout
00214         if (g_process_pending_source)
00215         { 
00216             g_source_remove(g_process_pending_source);
00217             g_process_pending_source = 0;
00218         }
00219 
00220         LOGPRINTF("received startupComplete from %s (pid %d, window %d), new state STATE_RUNNING", application, pid, window);
00221         if (strcmp(application, "ctb") == 0) {
00222             display_set_ctb_window(window);
00223         }
00224         
00225         // update process info
00226         proc->state = STATE_RUNNING;
00227         proc->is_multidoc = is_multidoc;
00228         proc->ipc_service = g_strdup(ipc_service);
00229 
00230         // do post startup actions
00231         post_process_startup(proc, application, window);
00232         
00233         // call post startup callback
00234         if (proc->startup_callback)
00235         {
00236             (proc->startup_callback)();
00237         }
00238 
00239         // start next process
00240         check_start_next();
00241         
00242         retval = TRUE;
00243     }
00244     return retval;
00245 }
00246 
00247 
00248 proc_t *process_get_by_pid(GPid pid)
00249 {
00250     LOGPRINTF("entry  pid [%d]", pid);
00251     
00252     GSList *proc_ptr = g_proclist;
00253     proc_t *cur_proc = NULL;
00254     
00255     while (proc_ptr)
00256     {
00257         cur_proc = (proc_t *) proc_ptr->data;
00258         
00259         if (cur_proc->pid == pid)
00260         {
00261             LOGPRINTF("found [%p] command [%s] ipc service [%s]", cur_proc, cur_proc->command, cur_proc->ipc_service);
00262             return cur_proc;
00263         }
00264             
00265         proc_ptr = proc_ptr->next;
00266     }
00267     
00268     return NULL;
00269 }
00270 
00271 #if (TESTING_ON)
00272 void print_process_list()
00273 {
00274     GSList *proc_ptr    = g_proclist;
00275 
00276     printf("%s() PROCESS LIST:\n", __func__);
00277     int i = 0;
00278     while (proc_ptr)
00279     {
00280         proc_t *cur = (proc_t *) proc_ptr->data;
00281         printf("  [%d] cmd=%s  ipc=%s  multi=%d  pid=%d\n", i,
00282             cur->command, cur->ipc_service, cur->is_multidoc, cur->pid);
00283 
00284         proc_ptr = proc_ptr->next;
00285         i++;
00286     }
00287 }
00288 #endif
00289 
00290 proc_t *process_get_by_name(const char *application)
00291 {
00292     LOGPRINTF("entry [%s]", application);
00293     
00294     GSList *proc_ptr    = g_proclist;
00295 
00296     while (proc_ptr)
00297     {
00298         proc_t *cur_proc = (proc_t *) proc_ptr->data;
00299 
00300         gint   argc         = 0;
00301         char   **argv_ptr   = NULL;
00302         if (g_shell_parse_argv(cur_proc->command, &argc, &argv_ptr, NULL))
00303         {
00304             gchar  *proc_app = g_path_get_basename(argv_ptr[0]);
00305 //            LOGPRINTF("check [%s]", proc_app);
00306             if (application && proc_app && (strcmp(proc_app, application) == 0))
00307             {
00308 //                LOGPRINTF("match [%s] pid [%d]", proc_app, cur_proc->pid);
00309                 g_free(proc_app);
00310                 return cur_proc;
00311             }
00312             g_free(proc_app);
00313             g_strfreev(argv_ptr);
00314         }
00315         proc_ptr = proc_ptr->next;
00316     }
00317     
00318     return NULL;
00319 }
00320 
00321 
00322 //============================================================================
00323 // Local Function Implementation
00324 //============================================================================
00325 
00326 static proc_t *create_process(const char* command, const char* working_dir, GCallback startup_callback, GCallback exit_callback, gint flags)
00327 {
00328     LOGPRINTF("entry: %s, %d", command, flags);
00329     
00330     proc_t *proc = g_new0 (proc_t, 1);
00331     if (!proc)
00332     {
00333         ERRORPRINTF("mem alloc failed");
00334         return NULL;
00335     }
00336     
00337     // init defaults
00338     //
00339     proc->command           = g_strdup(command);
00340     proc->working_dir       = NULL;
00341     if (working_dir)
00342     {
00343         proc->working_dir   = g_strdup(working_dir);
00344     }
00345     proc->ipc_service       = NULL;
00346     proc->pid               = 0;
00347     proc->is_multidoc       = FALSE;
00348     proc->flags             = flags;
00349     proc->startup_callback  = startup_callback;
00350     proc->exit_callback     = exit_callback;
00351     proc->state             = STATE_IDLE;
00352     
00353     // add to list
00354     g_proclist = g_slist_append(g_proclist, proc);
00355     
00356     return proc;
00357 }
00358 
00359 
00360 static void destroy_process(proc_t *proc)
00361 {
00362     // remove from list
00363     g_proclist = g_slist_remove(g_proclist, proc);
00364 
00365     // free memory
00366     g_free(proc->command);
00367     g_free(proc->ipc_service);
00368     g_free(proc->working_dir);
00369     g_free(proc);
00370     proc = NULL;
00371 }    
00372 
00373 
00374 static void on_process_exit(GPid pid, gint status, gpointer data)
00375 {
00376     LOGPRINTF("entry");
00377     
00378 #if (WARNING_ON)
00379     // see signal.h and man 3 waitpid
00380     const char *status_text[]  = { "", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL",
00381                                    "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE",
00382                                    "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE",
00383                                    "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD", 0};
00384     if (WIFSIGNALED(status))
00385    {
00386         WARNPRINTF("pid %d was terminated by signal %d [%s]", pid, WTERMSIG(status), 
00387             WTERMSIG(status) <= 17 ? status_text[WTERMSIG(status)] : "");
00388     }
00389     else if (WIFEXITED(status))
00390     {
00391         WARNPRINTF("pid %d has exited with status %d", pid, WEXITSTATUS(status));
00392     }
00393     else
00394     {
00395         // it's not likely this will ever be reached, but just to be sure
00396         WARNPRINTF("pid %d is gone, status %d", pid, status);
00397     }
00398 #endif
00399 
00400     proc_t *proc = data;
00401     gboolean try_respawn = TRUE;
00402     
00403     g_return_if_fail(proc != NULL);
00404     g_return_if_fail(proc->pid == pid);
00405     gchar *proc_app = g_path_get_basename(proc->command);
00406 
00407     if (proc->state == STATE_STARTING)
00408     {
00409         // stop pending timeout
00410         if (g_process_pending_source)
00411         { 
00412             g_source_remove(g_process_pending_source);
00413             g_process_pending_source = 0;
00414         }
00415         
00416         // clear respawn bit
00417         // when application crashes while starting, it makes 
00418         // no sense to retry this over and over again.
00419         proc->flags &= ~(PS_RESPAWN);
00420         
00421         ERRORPRINTF("application [%s] exited", proc->command);
00422     }
00423     
00424     // Check if app is adobe-fulfill or downloadmgr
00425     // and notify ctb. Needed because these apps are Dialogs, so
00426     // ctb does not get a WindowActivated/Deactivated call
00427     if (proc_app && 
00428         ((strcmp(proc_app, "downloadmgr") == 0) || (strcmp(proc_app, "adobe-fulfill") == 0)))
00429     {
00430         ipc_refresh_ctb();
00431     }
00432 
00433     // remove tasks for this process
00434     task_cleanup_pid(pid);
00435 
00436     // remove busy for this process
00437     busy_reset_pid(pid);
00438     
00439     // remove display info for this process
00440     display_cleanup_pid(pid);
00441     
00442     // remove connection when process died
00443     if ((status !=0) && proc->ipc_service)
00444     {
00445         conn_disconnect(proc->ipc_service);
00446     }
00447     
00448     // call post exit callback
00449     if (proc->exit_callback)
00450     {
00451         (proc->exit_callback)();
00452     }
00453 
00454     if (((WIFEXITED(status) && WEXITSTATUS(status) == 1) ||
00455          (WIFSIGNALED(status) && 
00456          (WTERMSIG(status) == SIGFPE || WTERMSIG(status) == SIGABRT || WTERMSIG(status) == SIGSEGV))) &&
00457           proc_app)
00458     {
00459         // unexpected error ocurred
00460         if (strcmp(proc_app, "uds") == 0)
00461         {
00462             if (sys_get_card() == STATE_CARD_INDEXING)
00463             {
00464                 // indexer caused UDS to fail, terminate it to prevent more errors
00465                 system("killall mdbindex");
00466                 ipc_show_message("indexerror", on_reply_error, proc);
00467                 // delay respawning UDS until user confirmed error dialog
00468                 try_respawn = FALSE;
00469             }
00470             else
00471             {
00472                 ipc_show_message("udserror", on_reply_error, proc);
00473                 // delay respawning UDS until user confirmed error dialog
00474                 try_respawn = FALSE;
00475             }
00476         }
00477         else if (strcmp(proc_app, "erbrowser") == 0)
00478         {
00479             erkeyb_client_hide();
00480             ipc_show_message("browsererror", NULL, NULL);
00481         }
00482     }
00483     
00484     if (try_respawn)
00485     {
00486         check_respawn(proc);
00487     }
00488     g_free(proc_app);
00489 }
00490 
00491 
00492 static void check_respawn(proc_t *proc)
00493 {
00494     GPid pid = proc->pid;
00495     
00496     if (proc->flags & PS_RESPAWN)
00497     {
00498         LOGPRINTF("should respawn, new state STATE_IDLE");
00499         WARNPRINTF("respawn application %s", proc->command);
00500         // reset to initial values
00501         proc->state = STATE_IDLE;
00502         proc->pid   = 0;
00503         g_free(proc->ipc_service);
00504         proc->ipc_service = NULL;
00505         // don't use callbacks when respawning
00506         proc->startup_callback = NULL;
00507         proc->exit_callback = NULL;
00508     }
00509     else
00510     {
00511         LOGPRINTF("no respawn, remove from list");
00512         destroy_process(proc);
00513         proc = NULL;
00514     }
00515     
00516     // graciously close pid
00517     g_spawn_close_pid(pid);
00518     
00519     if (sys_get_device_state() != STATE_DEVICE_STOPPING)
00520     {
00521         // start next process
00522         check_start_next();
00523     }
00524 }
00525 
00526 
00527 static gboolean on_startup_completed_timeout(gpointer data)
00528 {
00529     proc_t *proc = data;
00530     
00531     LOGPRINTF("entry");
00532     LOGPRINTF("timeout waiting for startupComplete (%s), new state STATE_RUNNING", proc->command);
00533     
00534     // callback startupComplete not received, assume it is running 
00535     proc->state = STATE_RUNNING;
00536     
00537     // start next process
00538     check_start_next();
00539     
00540     g_process_pending_source = 0;
00541     
00542     // stop and destroy this timer
00543     return FALSE; 
00544 }
00545 
00546 
00547 static void check_start_next()
00548 {
00549     LOGPRINTF("entry");
00550     
00551     proc_t *cur_proc = NULL;
00552     GSList *proc_ptr = g_proclist;
00553     gboolean is_ready = TRUE;
00554     
00555     if (sys_get_device_state() == STATE_DEVICE_STOPPING)
00556     {
00557         WARNPRINTF("device is shutting down, process not started");
00558         return;
00559     }
00560     
00561     while (proc_ptr)
00562     {
00563         cur_proc = (proc_t *) proc_ptr->data;
00564         
00565         if (((cur_proc->state == STATE_STARTING) && (cur_proc->flags & PS_WAIT_STARTED)) ||
00566             ((cur_proc->state == STATE_RUNNING) && (cur_proc->flags & PS_WAIT_EXIT)))
00567         {
00568             LOGPRINTF("wait for process %d to complete", cur_proc->pid);
00569             is_ready = FALSE;
00570             break;
00571         }
00572             
00573         proc_ptr = proc_ptr->next;
00574     }
00575     
00576     if (is_ready)
00577     {
00578         proc_ptr = g_proclist;
00579         
00580         while (proc_ptr)
00581         {
00582             cur_proc = (proc_t *) proc_ptr->data;
00583             
00584             if (cur_proc->state == STATE_IDLE)
00585             {
00586                 LOGPRINTF("start next process: %s", cur_proc->command);
00587                 start_process(cur_proc);
00588                 return;
00589             }
00590                 
00591             proc_ptr = proc_ptr->next;
00592         }
00593     }
00594     return;
00595 }
00596 
00597 
00598 static gboolean start_process(proc_t *proc)
00599 {
00600     LOGPRINTF("entry");
00601     
00602     GPid child_pid   = 0;
00603     gint argc        = 0;
00604     char **argv_ptr  = NULL;
00605     gboolean retval  = FALSE;
00606     GError *error    = NULL;
00607 
00608     LOGPRINTF("Starting: %s", proc->command);
00609 
00610     retval = g_shell_parse_argv(proc->command, &argc, &argv_ptr, &error);
00611     if (error)
00612     {
00613         WARNPRINTF("Error parsing command '%s' failed with error: %s", proc->command, error->message);
00614         g_error_free(error);
00615         return FALSE;
00616     }
00617     
00618     retval = g_spawn_async(proc->working_dir, argv_ptr, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &child_pid, &error);
00619     if (error)
00620     {
00621         WARNPRINTF("Execution of '%s' failed with error: %s", proc->command, error->message);
00622         g_error_free(error);
00623         if (argv_ptr) g_strfreev(argv_ptr);
00624         return FALSE;
00625     }
00626     
00627     if (proc->flags & PS_WAIT_STARTED)
00628     {
00629         if (g_process_pending_source)
00630         { 
00631             g_source_remove(g_process_pending_source);
00632             WARNPRINTF("A process is started while a previous one was still pending. Previous pending timer stopped.");
00633         }
00634         
00635         // set maximum time to wait for startupComplete
00636         g_process_pending_source = g_timeout_add_seconds(TIMEOUT_STARTUP_COMPLETED, on_startup_completed_timeout, proc);
00637         
00638         // update process entry
00639         LOGPRINTF("wait for startupComplete (pid %d), new state STATE_STARTING", child_pid);
00640         proc->state = STATE_STARTING;
00641         proc->pid = child_pid;
00642     }
00643     else
00644     {
00645         // update process entry
00646         LOGPRINTF("don't wait for startup, new state STATE_RUNNING");
00647         proc->state = STATE_RUNNING;
00648         proc->pid = child_pid;
00649         
00650         if (!(proc->flags & PS_WAIT_EXIT))
00651         {
00652             // start next process
00653             check_start_next();
00654         }
00655     }
00656     
00657     // watch child lifetime
00658     g_child_watch_add(child_pid, on_process_exit, proc);
00659     
00660     g_strfreev(argv_ptr);
00661     return TRUE;    
00662 }
00663 
00664 
00665 static void post_process_startup(proc_t *proc, const char *application, gint window)
00666 {
00667     LOGPRINTF("entry");
00668 
00669     // inform tasks of new window
00670     task_startup_completed(proc, window);
00671 
00672     // report mountpoint when card is available
00673     if (sys_get_card() == STATE_CARD_MOUNTED)
00674     {
00675         ipc_send_volume_mounted_to(proc->ipc_service, MOUNTPOINT_CARD);
00676     }
00677     
00678     // report current language as it may have changed during startup
00679     const char *locale = g_getenv("LANG");
00680     if (locale)
00681     {
00682         ipc_send_changed_locale(locale);
00683     }
00684 }
00685 
00686 
00687 static void on_reply_error(eripc_context_t *context,
00688                            const eripc_event_info_t *info,
00689                            void *user_data)
00690 {
00691     LOGPRINTF("entry");
00692     
00693     const eripc_arg_t *arg_array = info->args;
00694     
00695     if (info->event_type != ERIPC_EVENT_REPLY)
00696     {
00697         WARNPRINTF("invalid event: %d", info->event_type);
00698     }
00699     else if ((arg_array == NULL) || (arg_array[0].type != ERIPC_TYPE_BOOL))
00700     {
00701         WARNPRINTF("invalid arguments in reply");
00702     }
00703     else 
00704     {
00705         LOGPRINTF("User clicked ok");
00706         proc_t *proc = user_data;
00707         check_respawn(proc);
00708     }
00709 }
Generated by  doxygen 1.6.2-20100208