2007-07-11 20:03:18 +04:00
/*
* $ Id $
*
* Evoke , head honcho of everything
* Part of Equinox Desktop Environment ( EDE ) .
* Copyright ( c ) 2000 - 2007 EDE Authors .
*
* This program is licensed under terms of the
* GNU General Public License version 2 or newer .
* See COPYING for details .
*/
# include "Log.h"
2007-07-31 19:37:12 +04:00
# include "Logout.h"
2007-07-11 20:03:18 +04:00
# include "EvokeService.h"
2007-07-30 17:25:13 +04:00
# include "Splash.h"
# include "Spawn.h"
2007-08-06 16:39:15 +04:00
# include "Crash.h"
2007-07-30 17:25:13 +04:00
2007-07-11 20:03:18 +04:00
# include <edelib/File.h>
# include <edelib/Config.h>
2007-07-31 19:37:12 +04:00
# include <edelib/DesktopFile.h>
# include <edelib/Directory.h>
2007-07-11 20:03:18 +04:00
# include <edelib/StrUtil.h>
2007-07-30 17:25:13 +04:00
# include <edelib/Util.h>
# include <edelib/Debug.h>
2007-07-31 19:37:12 +04:00
# include <edelib/Nls.h>
2007-07-30 17:25:13 +04:00
# include <FL/fl_ask.h>
2007-07-11 20:03:18 +04:00
# include <sys/types.h> // getpid
# include <unistd.h> //
# include <stdlib.h> // free
2007-07-31 19:37:12 +04:00
# include <string.h> // strdup, memset
2007-08-07 15:54:57 +04:00
# include <errno.h> // error codes
2007-07-11 20:03:18 +04:00
2007-07-30 17:25:13 +04:00
void resolve_path ( const edelib : : String & imgdir , edelib : : String & img , bool have_imgdir ) {
if ( img . empty ( ) )
return ;
const char * im = img . c_str ( ) ;
if ( ! edelib : : file_exists ( im ) & & have_imgdir ) {
img = edelib : : build_filename ( " / " , imgdir . c_str ( ) , im ) ;
im = img . c_str ( ) ;
if ( ! edelib : : file_exists ( im ) ) {
// no file, send then empty
img . clear ( ) ;
}
}
}
int get_int_property_value ( Atom at ) {
Atom real ;
int format ;
unsigned long n , extra ;
unsigned char * prop ;
int status = XGetWindowProperty ( fl_display , RootWindow ( fl_display , fl_screen ) ,
at , 0L , 0x7fffffff , False , XA_CARDINAL , & real , & format , & n , & extra ,
( unsigned char * * ) & prop ) ;
int ret = - 1 ;
if ( status ! = Success & & ! prop )
return ret ;
ret = int ( * ( long * ) prop ) ;
XFree ( prop ) ;
return ret ;
}
int get_string_property_value ( Atom at , char * txt , int txt_len ) {
XTextProperty names ;
XGetTextProperty ( fl_display , RootWindow ( fl_display , fl_screen ) , & names , at ) ;
if ( ! names . nitems | | ! names . value )
return 0 ;
2007-07-11 20:03:18 +04:00
2007-07-30 17:25:13 +04:00
char * * vnames ;
int nsz = 0 ;
if ( ! XTextPropertyToStringList ( & names , & vnames , & nsz ) ) {
XFree ( names . value ) ;
return 0 ;
}
strncpy ( txt , vnames [ 0 ] , txt_len ) ;
txt [ txt_len ] = ' \0 ' ;
XFreeStringList ( vnames ) ;
return 1 ;
}
2007-08-06 16:39:15 +04:00
void service_watcher_cb ( int pid , int signum ) {
EvokeService : : instance ( ) - > service_watcher ( pid , signum ) ;
}
2007-07-30 17:25:13 +04:00
EvokeService : : EvokeService ( ) : is_running ( 0 ) , logfile ( NULL ) , pidfile ( NULL ) , lockfile ( NULL ) {
top_win = NULL ;
2007-07-11 20:03:18 +04:00
}
EvokeService : : ~ EvokeService ( ) {
if ( logfile )
delete logfile ;
2007-07-30 17:25:13 +04:00
if ( lockfile ) {
edelib : : file_remove ( lockfile ) ;
free ( lockfile ) ;
}
2007-07-11 20:03:18 +04:00
if ( pidfile ) {
edelib : : file_remove ( pidfile ) ;
free ( pidfile ) ;
}
2007-08-06 16:39:15 +04:00
processes . clear ( ) ;
2007-07-11 20:03:18 +04:00
}
EvokeService * EvokeService : : instance ( void ) {
static EvokeService es ;
return & es ;
}
bool EvokeService : : setup_logging ( const char * file ) {
if ( ! file )
logfile = new DummyLog ( ) ;
else
logfile = new RealLog ( ) ;
if ( ! logfile - > open ( file ) ) {
delete logfile ;
return false ;
}
return true ;
}
2007-07-30 17:25:13 +04:00
bool EvokeService : : setup_pid ( const char * file , const char * lock ) {
2007-07-11 20:03:18 +04:00
if ( ! file )
return false ;
2007-07-30 17:25:13 +04:00
if ( edelib : : file_exists ( lock ) )
return false ;
FILE * l = fopen ( lock , " w " ) ;
if ( ! l )
2007-07-11 20:03:18 +04:00
return false ;
2007-07-30 17:25:13 +04:00
fprintf ( l , " " ) ;
fclose ( l ) ;
lockfile = strdup ( lock ) ;
2007-07-11 20:03:18 +04:00
FILE * f = fopen ( file , " w " ) ;
if ( ! f )
return false ;
fprintf ( f , " %i " , getpid ( ) ) ;
fclose ( f ) ;
pidfile = strdup ( file ) ;
return true ;
}
2007-07-30 17:25:13 +04:00
bool EvokeService : : init_splash ( const char * config , bool no_splash , bool dry_run ) {
2007-07-11 20:03:18 +04:00
edelib : : Config c ;
if ( ! c . load ( config ) )
return false ;
char buff [ 1024 ] ;
2007-07-30 17:25:13 +04:00
bool have_imgdir = false ;
c . get ( " evoke " , " ImagesDirectory " , buff , sizeof ( buff ) ) ;
// no evoke section
if ( c . error ( ) = = edelib : : CONF_ERR_SECTION )
return false ;
edelib : : String imgdir ;
if ( c . error ( ) = = edelib : : CONF_SUCCESS ) {
imgdir = buff ;
have_imgdir = true ;
}
edelib : : String splashimg ;
if ( c . get ( " evoke " , " Splash " , buff , sizeof ( buff ) ) )
splashimg = buff ;
// Startup key must exists
2007-07-11 20:03:18 +04:00
if ( ! c . get ( " evoke " , " Startup " , buff , sizeof ( buff ) ) )
return false ;
2007-07-30 17:25:13 +04:00
StringList vs ;
2007-07-11 20:03:18 +04:00
edelib : : stringtok ( vs , buff , " , " ) ;
// nothing, fine, do nothing
unsigned int sz = vs . size ( ) ;
if ( sz = = 0 )
return true ;
EvokeClient ec ;
const char * key_name ;
2007-07-30 17:25:13 +04:00
for ( StringListIter it = vs . begin ( ) ; it ! = vs . end ( ) ; + + it ) {
key_name = ( * it ) . c_str ( ) ;
2007-07-11 20:03:18 +04:00
edelib : : str_trim ( ( char * ) key_name ) ;
// probably listed but not the same key; also Exec value must exists
if ( ! c . get ( key_name , " Exec " , buff , sizeof ( buff ) ) )
continue ;
else
ec . exec = buff ;
2007-07-30 17:25:13 +04:00
if ( c . get ( key_name , " Description " , buff , sizeof ( buff ) ) )
ec . desc = buff ;
2007-07-11 20:03:18 +04:00
if ( c . get ( key_name , " Icon " , buff , sizeof ( buff ) ) )
ec . icon = buff ;
clients . push_back ( ec ) ;
}
2007-07-30 17:25:13 +04:00
/*
* Now , before data is send to Splash , resolve directories
* since Splash expects that .
*/
resolve_path ( imgdir , splashimg , have_imgdir ) ;
for ( ClientListIter it = clients . begin ( ) ; it ! = clients . end ( ) ; + + it )
resolve_path ( imgdir , ( * it ) . icon , have_imgdir ) ;
Splash sp ( no_splash , dry_run ) ;
sp . set_clients ( & clients ) ;
sp . set_background ( & splashimg ) ;
sp . run ( ) ;
2007-07-11 20:03:18 +04:00
return true ;
}
2007-07-31 19:37:12 +04:00
/*
* This is implementation of Autostart Spec ( http : //standards.freedesktop.org/autostart-spec/autostart-spec-0.5.html).
* The Autostart Directories are $ XDG_CONFIG_DIRS / autostart .
* If the same filename is located under multiple Autostart Directories
* only the file under the most important directory should be used .
* Example : If $ XDG_CONFIG_HOME is not set the Autostart Directory in the user ' s home
* directory is ~ / . config / autostart /
* Example : If $ XDG_CONFIG_DIRS is not set the system wide Autostart Directory
* is / etc / xdg / autostart /
* Example : If $ XDG_CONFIG_HOME and $ XDG_CONFIG_DIRS are not set and the two
* files / etc / xdg / autostart / foo . desktop and ~ / . config / autostart / foo . desktop exist
* then only the file ~ / . config / autostart / foo . desktop will be used because ~ / . config / autostart /
* is more important than / etc / xdg / autostart /
* If Hidden key is set true in . desktop file , file MUST be ignored .
* OnlyShowIn and NotShowIn ( list of strings identifying desktop environments ) if ( or if not )
* contains environment name , MUST not be started / not started .
* TryExec is same as for . desktop spec .
*/
void EvokeService : : init_autostart ( void ) {
edelib : : String home = edelib : : user_config_dir ( ) ;
home + = " /autostart/ " ;
2007-08-06 16:39:15 +04:00
// TODO
2007-07-31 19:37:12 +04:00
}
2007-07-11 20:03:18 +04:00
void EvokeService : : setup_atoms ( Display * d ) {
2007-07-30 17:25:13 +04:00
// with them must be send '1' or property will be ignored (except _EDE_EVOKE_SPAWN)
2007-07-11 20:03:18 +04:00
_ede_shutdown_all = XInternAtom ( d , " _EDE_EVOKE_SHUTDOWN_ALL " , False ) ;
2007-07-30 17:25:13 +04:00
_ede_evoke_quit = XInternAtom ( d , " _EDE_EVOKE_QUIT " , False ) ;
2007-07-11 20:03:18 +04:00
_ede_spawn = XInternAtom ( d , " _EDE_EVOKE_SPAWN " , False ) ;
}
2007-07-31 19:37:12 +04:00
void EvokeService : : quit_x11 ( void ) {
int ret = fl_ask ( _ ( " Nice quitting is not implemented yet and this will forcefully kill \n all running applications. Make sure to save what needs to be saved. \n So, would you like to continue ? " ) ) ;
if ( ret )
stop ( ) ;
#if 0
/*
* This code is working , but not as I would like to see .
* It will ( mostly ) call XKillClient ( ) for _every_ window
* ( including i . e . buttons in some app ) , and that is not
* nice way to say good bye . This must be implemented in
* wm since it only knows what is actuall window and what not .
* For now quit_x11 ( ) will simply quit itself , quitting X11
* session ( if it holds it ) , which will in turn forcefully kill
* all windows .
*/
Window dummy , * wins ;
Window root = RootWindow ( fl_display , fl_screen ) ;
unsigned int n ;
if ( ! XQueryTree ( fl_display , root , & dummy , & dummy , & wins , & n ) )
return ;
Atom _wm_protocols = XInternAtom ( fl_display , " WM_PROTOCOLS " , False ) ;
Atom _wm_delete_window = XInternAtom ( fl_display , " WM_DELETE_WINDOW " , False ) ;
Atom * protocols ;
int np ;
XEvent ev ;
bool have_quit = 0 ;
for ( unsigned int i = 0 ; i < n ; i + + ) {
if ( wins [ i ] = = root )
continue ;
have_quit = 0 ;
if ( XGetWMProtocols ( fl_display , wins [ i ] , & protocols , & np ) ) {
for ( int j = 0 ; j < np ; j + + ) {
if ( protocols [ j ] = = _wm_delete_window ) {
have_quit = 1 ;
break ;
}
}
}
if ( have_quit ) {
memset ( & ev , 0 , sizeof ( ev ) ) ;
ev . xclient . type = ClientMessage ;
ev . xclient . window = wins [ i ] ;
ev . xclient . message_type = _wm_protocols ;
ev . xclient . format = 32 ;
ev . xclient . data . l [ 0 ] = ( long ) _wm_delete_window ;
ev . xclient . data . l [ 1 ] = ( long ) fl_event_time ;
XSendEvent ( fl_display , wins [ i ] , False , 0L , & ev ) ;
} else {
XKillClient ( fl_display , wins [ i ] ) ;
}
}
XFree ( ( void * ) wins ) ;
stop ( ) ;
# endif
}
2007-08-06 16:39:15 +04:00
/*
* Monitor starting service and report if staring
* failed . Also if one of runned services crashed
* attach gdb on it pid and run backtrace .
*/
void EvokeService : : service_watcher ( int pid , int signum ) {
2007-08-07 15:54:57 +04:00
printf ( " got %i \n " , signum ) ;
if ( signum = = SPAWN_CHILD_CRASHED ) {
2007-08-06 16:39:15 +04:00
EvokeProcess pc ;
2007-08-07 15:54:57 +04:00
2007-08-06 16:39:15 +04:00
if ( find_and_unregister_process ( pid , pc ) ) {
printf ( " %s crashed with core dump \n " , pc . cmd . c_str ( ) ) ;
CrashDialog cdialog ;
cdialog . set_data ( pc . cmd . c_str ( ) ) ;
cdialog . run ( ) ;
return ;
}
2007-08-07 15:54:57 +04:00
} else if ( signum = = SPAWN_CHILD_KILLED ) {
printf ( " child %i killed \n " , pid ) ;
} else {
printf ( " child %i exited with %i \n " , pid , signum ) ;
2007-08-06 16:39:15 +04:00
}
unregister_process ( pid ) ;
}
2007-08-07 15:54:57 +04:00
/*
* Execute program . It ' s return status
* will be reported via service_watcher ( )
*/
void EvokeService : : run_program ( const char * cmd ) {
EASSERT ( cmd ! = NULL ) ;
pid_t child ;
int r = spawn_program_with_core ( cmd , service_watcher_cb , & child ) ;
if ( r ! = 0 )
fl_alert ( " Unable to start %s. Got code %i " , cmd , r ) ;
else
register_process ( cmd , child ) ;
}
2007-08-06 16:39:15 +04:00
void EvokeService : : register_process ( const char * cmd , pid_t pid ) {
EvokeProcess pc ;
pc . cmd = cmd ;
pc . pid = pid ;
printf ( " registering %s with %i \n " , cmd , pid ) ;
processes . push_back ( pc ) ;
}
void EvokeService : : unregister_process ( pid_t pid ) {
if ( processes . empty ( ) )
return ;
ProcessListIter it = processes . begin ( ) ;
ProcessListIter it_end = processes . end ( ) ;
while ( it ! = it_end ) {
if ( ( * it ) . pid = = pid ) {
printf ( " Found %s with pid %i, cleaning... \n " , ( * it ) . cmd . c_str ( ) , pid ) ;
processes . erase ( it ) ;
return ;
}
+ + it ;
}
}
bool EvokeService : : find_and_unregister_process ( pid_t pid , EvokeProcess & pc ) {
if ( processes . empty ( ) )
return 0 ;
ProcessListIter it = processes . begin ( ) ;
ProcessListIter it_end = processes . end ( ) ;
while ( it ! = it_end ) {
if ( ( * it ) . pid = = pid ) {
printf ( " Found %s with pid %i, cleaning... \n " , ( * it ) . cmd . c_str ( ) , pid ) ;
pc . cmd = ( * it ) . cmd ;
pc . pid = pid ;
processes . erase ( it ) ;
return 1 ;
}
+ + it ;
}
return 0 ;
}
2007-07-30 17:25:13 +04:00
int EvokeService : : handle ( const XEvent * ev ) {
logfile - > printf ( " Got event %i \n " , ev - > type ) ;
if ( ev - > type = = MapNotify ) {
if ( top_win ) {
// for splash to keep it at top (working in old edewm)
XRaiseWindow ( fl_display , fl_xid ( top_win ) ) ;
return 1 ;
}
} else if ( ev - > type = = ConfigureNotify ) {
if ( ev - > xconfigure . event = = DefaultRootWindow ( fl_display ) & & top_win ) {
// splash too, but keep window under other wm's
XRaiseWindow ( fl_display , fl_xid ( top_win ) ) ;
return 1 ;
}
} else if ( ev - > type = = PropertyNotify ) {
if ( ev - > xproperty . atom = = _ede_spawn ) {
char buff [ 1024 ] ;
if ( get_string_property_value ( _ede_spawn , buff , sizeof ( buff ) ) ) {
logfile - > printf ( " Got _EVOKE_SPAWN with %s. Starting client... \n " , buff ) ;
2007-08-07 15:54:57 +04:00
run_program ( buff ) ;
2007-08-06 16:39:15 +04:00
} else {
logfile - > printf ( " Got _EVOKE_SPAWN with malformed data. Ignoring... \n " ) ;
}
2007-07-30 17:25:13 +04:00
return 1 ;
}
if ( ev - > xproperty . atom = = _ede_evoke_quit ) {
int val = get_int_property_value ( _ede_evoke_quit ) ;
if ( val = = 1 ) {
logfile - > printf ( " Got accepted _EDE_EVOKE_QUIT \n " ) ;
stop ( ) ;
} else
logfile - > printf ( " Got _EDE_EVOKE_QUIT with bad code (%i). Ignoring... \n " , val ) ;
return 1 ;
}
if ( ev - > xproperty . atom = = _ede_shutdown_all ) {
int val = get_int_property_value ( _ede_shutdown_all ) ;
2007-07-31 19:37:12 +04:00
if ( val = = 1 ) {
2007-07-30 17:25:13 +04:00
logfile - > printf ( " Got accepted _EDE_EVOKE_SHUTDOWN_ALL \n " ) ;
2007-07-31 19:37:12 +04:00
int dw = DisplayWidth ( fl_display , fl_screen ) ;
int dh = DisplayHeight ( fl_display , fl_screen ) ;
printf ( " got %i \n " , logout_dialog ( dw , dh ) ) ;
2007-08-07 15:54:57 +04:00
// quit_x11();
2007-07-31 19:37:12 +04:00
} else
2007-07-30 17:25:13 +04:00
logfile - > printf ( " Got _EDE_EVOKE_SHUTDOWN_ALL with bad code (%i). Ignoring... \n " , val ) ;
return 1 ;
}
}
2007-07-11 20:03:18 +04:00
return 0 ;
}