diff --git a/medit/medit-app.c b/medit/medit-app.c index 2021510d..d3fb8ca8 100644 --- a/medit/medit-app.c +++ b/medit/medit-app.c @@ -79,6 +79,10 @@ int _medit_parse_options (const char *const program_name, #define STR_HELP_PID "\ --pid=PID Use existing instance with process id PID\n" +#define STR_HELP_APP_NAME "\ + --app-name=NAME Set instance name to NAME if it's not already\n\ + running\n" + #define STR_HELP_MODE "\ -m, --mode=[simple|project] Use specified mode\n" @@ -110,6 +114,8 @@ int _medit_parse_options (const char *const program_name, -n, --new-app Run new instance of application\n\ -s, --use-session Load and save session\n\ --pid=PID Use existing instance with process id PID\n\ + --app-name=NAME Set instance name to NAME if it's not already\n\ + running\n\ -m, --mode=[simple|project] Use specified mode\n\ -p, --project=PROJECT Open project file PROJECT\n\ -l, --line=LINE Open file and position cursor on line LINE\n\ @@ -129,6 +135,9 @@ char _medit_opt_use_session; /* Set to 1 if option --pid has been specified. */ char _medit_opt_pid; +/* Set to 1 if option --app-name has been specified. */ +char _medit_opt_app_name; + /* Set to 1 if option --mode (-m) has been specified. */ char _medit_opt_mode; @@ -159,6 +168,9 @@ char _medit_opt_help; /* Argument to option --pid. */ const char *_medit_arg_pid; +/* Argument to option --app-name. */ +const char *_medit_arg_app_name; + /* Argument to option --mode (-m). */ const char *_medit_arg_mode; @@ -184,6 +196,7 @@ int _medit_parse_options (const char *const program_name, const int argc, char * static const char *const optstr__new_app = "new-app"; static const char *const optstr__use_session = "use-session"; static const char *const optstr__pid = "pid"; + static const char *const optstr__app_name = "app-name"; static const char *const optstr__mode = "mode"; static const char *const optstr__project = "project"; static const char *const optstr__line = "line"; @@ -196,6 +209,7 @@ int _medit_parse_options (const char *const program_name, const int argc, char * _medit_opt_new_app = 0; _medit_opt_use_session = 0; _medit_opt_pid = 0; + _medit_opt_app_name = 0; _medit_opt_mode = 0; _medit_opt_project = 0; _medit_opt_line = 0; @@ -206,6 +220,7 @@ int _medit_parse_options (const char *const program_name, const int argc, char * _medit_opt_version = 0; _medit_opt_help = 0; _medit_arg_pid = 0; + _medit_arg_app_name = 0; _medit_arg_mode = 0; _medit_arg_project = 0; _medit_arg_line = 0; @@ -234,6 +249,22 @@ int _medit_parse_options (const char *const program_name, const int argc, char * { case '\0': return i + 1; + case 'a': + if (strncmp (option + 1, optstr__app_name + 1, option_len - 1) == 0) + { + if (argument != 0) + _medit_arg_app_name = argument; + else if (++i < argc) + _medit_arg_app_name = argv [i]; + else + { + option = optstr__app_name; + goto error_missing_arg_long; + } + _medit_opt_app_name = 1; + break; + } + goto error_unknown_long_opt; case 'd': if (strncmp (option + 1, optstr__debug + 1, option_len - 1) == 0) { @@ -473,12 +504,13 @@ int _medit_parse_options (const char *const program_name, const int argc, char * } return i; } -#line 63 "../../../medit/medit-app.opag" +#line 64 "../../../medit/medit-app.opag" #undef STR_HELP #define STR_HELP \ STR_HELP_NEW_APP \ STR_HELP_PID \ + STR_HELP_APP_NAME \ STR_HELP_LINE \ STR_HELP_LOG \ STR_HELP_DEBUG \ @@ -489,11 +521,21 @@ int _medit_parse_options (const char *const program_name, const int argc, char * ********************************************************/ -static void -usage (void) +G_GNUC_NORETURN static void +usage (int status) { - g_print ("Usage: %s [OPTIONS] [FILES]\n", g_get_prgname ()); - g_print ("Options:\n%s", STR_HELP); + if (status) + { + g_printerr ("Usage: %s [OPTIONS] [FILES]\n", g_get_prgname ()); + g_printerr ("Options:\n%s", STR_HELP); + } + else + { + g_print ("Usage: %s [OPTIONS] [FILES]\n", g_get_prgname ()); + g_print ("Options:\n%s", STR_HELP); + } + + exit (status); } static void @@ -507,10 +549,7 @@ static void check_args (int opt_remain) { if (_medit_opt_help) - { - usage (); - exit (0); - } + usage (0); if (_medit_opt_version) { @@ -519,8 +558,11 @@ check_args (int opt_remain) } if (opt_remain < 0) + usage (1); + + if (_medit_opt_pid && _medit_opt_app_name) { - usage (); + g_printerr ("--pid may not be used together with --app-name\n"); exit (1); } @@ -528,16 +570,16 @@ check_args (int opt_remain) { if (_medit_opt_new_app) { - g_print ("--new-app can't be used together with --pid\n"); + g_printerr ("--new-app can't be used together with --pid\n"); exit (1); } if (!_medit_arg_pid || !_medit_arg_pid[0]) - { - usage (); - exit (1); - } + usage (1); } + + if (_medit_opt_app_name && (!_medit_arg_app_name || !_medit_arg_app_name[0])) + usage (1); } @@ -602,8 +644,8 @@ main (int argc, char *argv[]) AppMode mode = MODE_SIMPLE; guint32 stamp; guint32 line = 0; - const char *pid_string = NULL; gboolean use_session = FALSE; + const char *name = NULL; init_mem_stuff (); @@ -649,8 +691,8 @@ main (int argc, char *argv[]) if (_medit_opt_new_app || mode == MODE_PROJECT) new_instance = TRUE; - run_input = !_medit_opt_new_app || _medit_opt_use_session || _medit_opt_project; - use_session = !_medit_opt_new_app || _medit_opt_use_session; + run_input = !_medit_opt_new_app || _medit_opt_app_name || _medit_opt_use_session || _medit_opt_project; + use_session = !_medit_opt_new_app || _medit_opt_use_session || _medit_opt_app_name; app = g_object_new (MOO_TYPE_APP, "argv", argv, @@ -663,37 +705,46 @@ main (int argc, char *argv[]) "quit-on-editor-close", TRUE, "logo", "medit", "credits", THANKS, + "instance-name", _medit_opt_app_name ? _medit_arg_app_name : NULL, NULL); if (_medit_arg_line) line = strtol (_medit_arg_line, NULL, 10); if (_medit_opt_pid) - pid_string = _medit_arg_pid; + name = _medit_arg_pid; + else if (_medit_opt_app_name) + name = _medit_arg_app_name; else if (!_medit_opt_new_app) - pid_string = g_getenv ("MEDIT_PID"); + name = g_getenv ("MEDIT_PID"); + + if (name && !name[0]) + name = NULL; if (_medit_opt_exec || _medit_opt_exec_file) { GString *msg; msg = g_string_new (_medit_opt_exec ? "p" : "P"); g_string_append (msg, _medit_opt_exec ? _medit_arg_exec : _medit_arg_exec_file); - moo_app_send_msg (app, pid_string, msg->str, msg->len + 1); + moo_app_send_msg (app, name, msg->str, msg->len + 1); exit (0); } files = moo_filenames_from_locale (argv + opt_remain); - if (pid_string) + if (name) { - if (moo_app_send_files (app, files, line, stamp, pid_string)) + if (moo_app_send_files (app, files, line, stamp, name)) exit (0); - g_print ("Could not send files to pid %s\n", pid_string); - exit (1); + if (!_medit_opt_app_name) + { + g_printerr ("Could not send files to instance '%s'\n", name); + exit (1); + } } - if ((!new_instance && moo_app_send_files (app, files, line, stamp, NULL)) || + if ((!new_instance && !_medit_opt_app_name && moo_app_send_files (app, files, line, stamp, NULL)) || !moo_app_init (app)) { gdk_notify_startup_complete (); diff --git a/medit/medit-app.opag b/medit/medit-app.opag index 1a4b8b34..a8e5d8d0 100644 --- a/medit/medit-app.opag +++ b/medit/medit-app.opag @@ -48,23 +48,25 @@ int _medit_parse_options (const char *const program_name, %% n new-app "Run new instance of application" s use-session "Load and save session" - pid "=PID Use existing instance with process id PID" reqarg -m mode "=[simple|project] Use specified mode" reqarg -p project "=PROJECT Open project file PROJECT" reqarg -l line "=LINE Open file and position cursor on line LINE" reqarg - log "[=FILE] Show debug output or write it to FILE" optarg + pid "=PID Use existing instance with process id PID" reqarg + app-name "=NAME Set instance name to NAME if it's not already running" reqarg +m mode "=[simple|project] Use specified mode" reqarg +p project "=PROJECT Open project file PROJECT" reqarg +l line "=LINE Open file and position cursor on line LINE" reqarg + log "[=FILE] Show debug output or write it to FILE" optarg debug "Run in debug mode" - exec "=CODE Execute python code in an existing instance" reqarg - exec-file "=FILE Execute python file in an existing instance" reqarg - version "Display version information and exit" return -h help "Display this help text and exit" return + exec "=CODE Execute python code in an existing instance" reqarg + exec-file "=FILE Execute python file in an existing instance" reqarg + version "Display version information and exit" return +h help "Display this help text and exit" return %% -#line 63 "../../../medit/medit-app.opag" +#line 64 "../../../medit/medit-app.opag" #undef STR_HELP #define STR_HELP \ STR_HELP_NEW_APP \ STR_HELP_PID \ + STR_HELP_APP_NAME \ STR_HELP_LINE \ STR_HELP_LOG \ STR_HELP_DEBUG \ @@ -75,11 +77,21 @@ h help "Display this help text and exit" return ********************************************************/ -static void -usage (void) +G_GNUC_NORETURN static void +usage (int status) { - g_print ("Usage: %s [OPTIONS] [FILES]\n", g_get_prgname ()); - g_print ("Options:\n%s", STR_HELP); + if (status) + { + g_printerr ("Usage: %s [OPTIONS] [FILES]\n", g_get_prgname ()); + g_printerr ("Options:\n%s", STR_HELP); + } + else + { + g_print ("Usage: %s [OPTIONS] [FILES]\n", g_get_prgname ()); + g_print ("Options:\n%s", STR_HELP); + } + + exit (status); } static void @@ -93,10 +105,7 @@ static void check_args (int opt_remain) { if (_medit_opt_help) - { - usage (); - exit (0); - } + usage (0); if (_medit_opt_version) { @@ -105,8 +114,11 @@ check_args (int opt_remain) } if (opt_remain < 0) + usage (1); + + if (_medit_opt_pid && _medit_opt_app_name) { - usage (); + g_printerr ("--pid may not be used together with --app-name\n"); exit (1); } @@ -114,16 +126,16 @@ check_args (int opt_remain) { if (_medit_opt_new_app) { - g_print ("--new-app can't be used together with --pid\n"); + g_printerr ("--new-app can't be used together with --pid\n"); exit (1); } if (!_medit_arg_pid || !_medit_arg_pid[0]) - { - usage (); - exit (1); - } + usage (1); } + + if (_medit_opt_app_name && (!_medit_arg_app_name || !_medit_arg_app_name[0])) + usage (1); } @@ -188,8 +200,8 @@ main (int argc, char *argv[]) AppMode mode = MODE_SIMPLE; guint32 stamp; guint32 line = 0; - const char *pid_string = NULL; gboolean use_session = FALSE; + const char *name = NULL; init_mem_stuff (); @@ -235,8 +247,8 @@ main (int argc, char *argv[]) if (_medit_opt_new_app || mode == MODE_PROJECT) new_instance = TRUE; - run_input = !_medit_opt_new_app || _medit_opt_use_session || _medit_opt_project; - use_session = !_medit_opt_new_app || _medit_opt_use_session; + run_input = !_medit_opt_new_app || _medit_opt_app_name || _medit_opt_use_session || _medit_opt_project; + use_session = !_medit_opt_new_app || _medit_opt_use_session || _medit_opt_app_name; app = g_object_new (MOO_TYPE_APP, "argv", argv, @@ -249,37 +261,46 @@ main (int argc, char *argv[]) "quit-on-editor-close", TRUE, "logo", "medit", "credits", THANKS, + "instance-name", _medit_opt_app_name ? _medit_arg_app_name : NULL, NULL); if (_medit_arg_line) line = strtol (_medit_arg_line, NULL, 10); if (_medit_opt_pid) - pid_string = _medit_arg_pid; + name = _medit_arg_pid; + else if (_medit_opt_app_name) + name = _medit_arg_app_name; else if (!_medit_opt_new_app) - pid_string = g_getenv ("MEDIT_PID"); + name = g_getenv ("MEDIT_PID"); + + if (name && !name[0]) + name = NULL; if (_medit_opt_exec || _medit_opt_exec_file) { GString *msg; msg = g_string_new (_medit_opt_exec ? "p" : "P"); g_string_append (msg, _medit_opt_exec ? _medit_arg_exec : _medit_arg_exec_file); - moo_app_send_msg (app, pid_string, msg->str, msg->len + 1); + moo_app_send_msg (app, name, msg->str, msg->len + 1); exit (0); } files = moo_filenames_from_locale (argv + opt_remain); - if (pid_string) + if (name) { - if (moo_app_send_files (app, files, line, stamp, pid_string)) + if (moo_app_send_files (app, files, line, stamp, name)) exit (0); - g_print ("Could not send files to pid %s\n", pid_string); - exit (1); + if (!_medit_opt_app_name) + { + g_printerr ("Could not send files to instance '%s'\n", name); + exit (1); + } } - if ((!new_instance && moo_app_send_files (app, files, line, stamp, NULL)) || + if ((!new_instance && !_medit_opt_app_name && moo_app_send_files (app, files, line, stamp, NULL)) || !moo_app_init (app)) { gdk_notify_startup_complete (); diff --git a/medit/medit.1 b/medit/medit.1 index ef49b3b9..2febe6ee 100644 --- a/medit/medit.1 +++ b/medit/medit.1 @@ -41,6 +41,10 @@ Load and save session. By default \fBmedit\fP does it when \-n is not used. .B \-\-pid PID Use existing instance with process id \fBPID\fP. .TP +.B \-\-app-name NAME +Use instance name \fBNAME\fP. If an instance with this name is already running, +then it will send files given on the command line to that instance and exit. +.TP .B \-l, \-\-line LINE Open file and position cursor on line \fBLINE\fP. .TP @@ -58,6 +62,7 @@ Execute python file in an existing instance. .TP .B files List of files to open. Filenames may include line numbers after colon, e.g. /tmp/file.txt:200. +Trailing colon is also fine. .SH ENVIRONMENT .TP .B MEDIT_PID diff --git a/medit/medit.desktop.in b/medit/medit.desktop.in index bbdc85a0..ac46e74e 100644 --- a/medit/medit.desktop.in +++ b/medit/medit.desktop.in @@ -8,4 +8,4 @@ Type=Application StartupNotify=true MimeType=text/plain; Icon=medit.png -Categories=Application;Utility;TextEditor; +Categories=Utility;TextEditor; diff --git a/moo.mprj b/moo.mprj index 927c3380..ec941e5b 100644 --- a/moo.mprj +++ b/moo.mprj @@ -13,8 +13,8 @@ --enable-debug=full --enable-all-warnings --enable-project - -g -g + -g @@ -37,8 +37,8 @@ build/optimized - -O2 -g -O2 -g + -O2 -g @@ -47,8 +47,8 @@ --enable-debug=full --enable-all-warnings --enable-project --prefix=/usr/local/test - -g -g + -g diff --git a/moo/mooapp/mooapp.c b/moo/mooapp/mooapp.c index 63e9ca20..967aa9df 100644 --- a/moo/mooapp/mooapp.c +++ b/moo/mooapp/mooapp.c @@ -71,7 +71,9 @@ struct _MooAppPrivate { MooEditor *editor; MooAppInfo *info; char *rc_files[2]; + gboolean run_input; + char *instance_name; gboolean running; gboolean in_try_quit; @@ -191,7 +193,8 @@ enum { PROP_LOGO, PROP_WEBSITE, PROP_WEBSITE_LABEL, - PROP_CREDITS + PROP_CREDITS, + PROP_INSTANCE_NAME }; enum { @@ -303,6 +306,14 @@ moo_app_class_init (MooAppClass *klass) TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (gobject_class, + PROP_INSTANCE_NAME, + g_param_spec_string ("instance-name", + "instance-name", + "instance-name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (gobject_class, PROP_USE_EDITOR, g_param_spec_boolean ("use-editor", @@ -510,6 +521,7 @@ moo_app_finalize (GObject *object) if (app->priv->ui_xml) g_object_unref (app->priv->ui_xml); + g_free (app->priv->instance_name); g_free (app->priv); G_OBJECT_CLASS (moo_app_parent_class)->finalize (object); @@ -561,6 +573,11 @@ moo_app_set_property (GObject *object, app->priv->run_input = g_value_get_boolean (value); break; + case PROP_INSTANCE_NAME: + g_free (app->priv->instance_name); + app->priv->instance_name = g_value_dup_string (value); + break; + case PROP_USE_EDITOR: app->priv->use_editor = g_value_get_boolean (value); break; @@ -625,6 +642,9 @@ moo_app_get_property (GObject *object, case PROP_RUN_INPUT: g_value_set_boolean (value, app->priv->run_input); break; + case PROP_INSTANCE_NAME: + g_value_set_string (value, app->priv->instance_name); + break; case PROP_USE_EDITOR: g_value_set_boolean (value, app->priv->use_editor); @@ -666,13 +686,6 @@ moo_app_set_exit_code (MooApp *app, } -const char * -moo_app_get_input_pipe_name (G_GNUC_UNUSED MooApp *app) -{ - return moo_app_input ? _moo_app_input_get_name (moo_app_input) : NULL; -} - - char * moo_app_create_user_data_dir (MooApp *app) { @@ -891,16 +904,9 @@ static void start_input (MooApp *app) { if (app->priv->run_input) - { - moo_app_input = _moo_app_input_new (app->priv->info->short_name); - - if (!_moo_app_input_start (moo_app_input)) - { - g_critical ("%s: oops", G_STRLOC); - _moo_app_input_free (moo_app_input); - moo_app_input = NULL; - } - } + moo_app_input = _moo_app_input_new (app->priv->info->short_name, + app->priv->instance_name, + TRUE); } @@ -1097,7 +1103,6 @@ moo_app_quit_real (MooApp *app) if (moo_app_input) { - _moo_app_input_shutdown (moo_app_input); _moo_app_input_free (moo_app_input); moo_app_input = NULL; } @@ -1541,7 +1546,15 @@ moo_app_load_session (MooApp *app) g_return_if_fail (MOO_IS_APP (app)); if (!app->priv->session_file) - app->priv->session_file = g_strdup_printf ("%s.session", g_get_prgname ()); + { + if (app->priv->instance_name) + app->priv->session_file = g_strdup_printf ("%s.session.%s", + g_get_prgname (), + app->priv->instance_name); + else + app->priv->session_file = g_strdup_printf ("%s.session", + g_get_prgname ()); + } session_file = moo_get_user_cache_file (app->priv->session_file); @@ -1877,8 +1890,18 @@ move_rc_files (MooApp *app) const char *new_file; char *old_file = g_strdup_printf ("%s/.%s.state", g_get_home_dir (), g_get_prgname ()); - app->priv->rc_files[MOO_PREFS_STATE] = - g_strdup_printf ("%s/%s.state", cache_dir, g_get_prgname ()); + if (app->priv->instance_name) + app->priv->rc_files[MOO_PREFS_STATE] = + g_strdup_printf ("%s/%s.state.%s", + cache_dir, + g_get_prgname (), + app->priv->instance_name); + else + app->priv->rc_files[MOO_PREFS_STATE] = + g_strdup_printf ("%s/%s.state", + cache_dir, + g_get_prgname ()); + new_file = app->priv->rc_files[MOO_PREFS_STATE]; if (!g_file_test (new_file, G_FILE_TEST_EXISTS) && diff --git a/moo/mooapp/mooapp.h b/moo/mooapp/mooapp.h index a7ac9a75..e1178957 100644 --- a/moo/mooapp/mooapp.h +++ b/moo/mooapp/mooapp.h @@ -92,7 +92,6 @@ void moo_app_set_exit_code (MooApp *app, const MooAppInfo*moo_app_get_info (MooApp *app); char *moo_app_create_user_data_dir (MooApp *app); -const char *moo_app_get_input_pipe_name (MooApp *app); MooEditor *moo_app_get_editor (MooApp *app); diff --git a/moo/mooapp/mooappinput.c b/moo/mooapp/mooappinput.c index 64328ed7..537d3b2b 100644 --- a/moo/mooapp/mooappinput.c +++ b/moo/mooapp/mooappinput.c @@ -16,19 +16,32 @@ #endif #ifdef __WIN32__ -#include -#include -#include -#else /* !__WIN32__ */ -#include -#include -#include +#define MOO_APP_INPUT_WIN32 +#elif defined(MOO_USE_PIPE_INPUT) +#define MOO_APP_INPUT_PIPE +#else +#define MOO_APP_INPUT_SOCKET +#endif + +#if defined(MOO_APP_INPUT_WIN32) +# include +# include +#elif defined(MOO_APP_INPUT_PIPE) +# include +# include +#else +# include +# include +#endif + +#ifndef __WIN32__ #include #include -#include -#include -#endif /* !__WIN32__ */ +#include +#include +#endif +#include #include #include #include @@ -39,145 +52,94 @@ #include "mooutils/mooutils-thread.h" +#define MAX_BUFFER_SIZE 4096 + +#ifdef MOO_APP_INPUT_SOCKET +#define INPUT_PREFIX "in-" +#else +#define INPUT_PREFIX "input-" +#endif + +typedef struct InputChannel InputChannel; + struct _MooAppInput { - int pipe; - char *pipe_basename; - char *pipe_name; - char *pipe_dir; - GIOChannel *io; - guint io_watch; - GString *buffer; /* messages are zero-terminated */ - gboolean ready; - -#ifdef __WIN32__ - guint event_id; -#endif /* __WIN32__ */ + GSList *pipes; + char *appname; }; - -#define MAX_BUFFER_SIZE 4096 +static InputChannel *input_channel_new (const char *appname, + const char *name, + gboolean may_fail); +static void input_channel_free (InputChannel *ch); MooAppInput * -_moo_app_input_new (const char *pipe_basename) +_moo_app_input_new (const char *appname, + const char *name, + gboolean bind_default) { MooAppInput *ch; + InputChannel *ich; - g_return_val_if_fail (pipe_basename != NULL, NULL); + g_return_val_if_fail (appname != NULL, NULL); - ch = g_new0 (MooAppInput, 1); + ch = _moo_new0 (MooAppInput); - ch->pipe_basename = g_strdup (pipe_basename); + ch->pipes = NULL; + ch->appname = g_strdup (appname); -#ifndef __WIN32__ - ch->pipe = -1; -#endif /* ! __WIN32__ */ - ch->pipe_name = NULL; - ch->io = NULL; - ch->io_watch = 0; - ch->ready = FALSE; - ch->buffer = g_string_new_len (NULL, MAX_BUFFER_SIZE); + if ((ich = input_channel_new (appname, _moo_get_pid_string (), FALSE))) + ch->pipes = g_slist_prepend (ch->pipes, ich); + + if (name && (ich = input_channel_new (appname, name, FALSE))) + ch->pipes = g_slist_prepend (ch->pipes, ich); + + if (bind_default && (ich = input_channel_new (appname, MOO_APP_INPUT_NAME_DEFAULT, TRUE))) + ch->pipes = g_slist_prepend (ch->pipes, ich); return ch; } - void _moo_app_input_free (MooAppInput *ch) { g_return_if_fail (ch != NULL); - _moo_app_input_shutdown (ch); + g_slist_foreach (ch->pipes, (GFunc) input_channel_free, NULL); - g_string_free (ch->buffer, TRUE); - g_free (ch->pipe_basename); - g_free (ch); -} - - -void -_moo_app_input_shutdown (MooAppInput *ch) -{ - g_return_if_fail (ch != NULL); - -#ifdef __WIN32__ - if (ch->event_id) - { - _moo_event_queue_disconnect (ch->event_id); - ch->event_id = 0; - } -#endif /* __WIN32__ */ - - if (ch->io) - { - g_io_channel_shutdown (ch->io, TRUE, NULL); - g_io_channel_unref (ch->io); - ch->io = NULL; - } - - if (ch->pipe_name) - { -#ifndef __WIN32__ - ch->pipe = -1; - unlink (ch->pipe_name); -#endif /* ! __WIN32__ */ - g_free (ch->pipe_name); - ch->pipe_name = NULL; - } - - if (ch->pipe_dir) - { - remove (ch->pipe_dir); - g_free (ch->pipe_dir); - ch->pipe_dir = NULL; - } - - if (ch->io_watch) - { - g_source_remove (ch->io_watch); - ch->io_watch = 0; - } - - ch->ready = FALSE; -} - - -const char * -_moo_app_input_get_name (MooAppInput *ch) -{ - g_return_val_if_fail (ch != NULL, NULL); - return ch->pipe_name; + g_free (ch->appname); + _moo_free (MooAppInput, ch); } static void -commit (MooAppInput *self) +commit (GString **buffer) { char buf[MAX_BUFFER_SIZE]; GString *freeme = NULL; char *ptr; gsize len; - if (!self->buffer->len) + if (!(*buffer)->len) { - g_warning ("%s: got empty command", G_STRLOC); + _moo_message ("%s: got empty command", G_STRLOC); return; } - if (self->buffer->len + 1 > MAX_BUFFER_SIZE) + if ((*buffer)->len + 1 > MAX_BUFFER_SIZE) { - freeme = self->buffer; - self->buffer = g_string_new_len (NULL, MAX_BUFFER_SIZE); + freeme = *buffer; + *buffer = g_string_new_len (NULL, MAX_BUFFER_SIZE); ptr = freeme->str; len = freeme->len; } else { - memcpy (buf, self->buffer->str, self->buffer->len + 1); + memcpy (buf, (*buffer)->str, (*buffer)->len + 1); ptr = buf; - len = self->buffer->len; - g_string_truncate (self->buffer, 0); + len = (*buffer)->len; + g_string_truncate (*buffer, 0); } if (0) @@ -190,17 +152,830 @@ commit (MooAppInput *self) } +#ifndef MOO_APP_INPUT_WIN32 + +static char * +get_pipe_dir (const char *appname) +{ + GdkDisplay *display; + char *display_name; + char *user_name; + char *name; + + g_return_val_if_fail (appname != NULL, NULL); + + display = gdk_display_get_default (); + g_return_val_if_fail (display != NULL, NULL); + + display_name = g_strcanon (g_strdup (gdk_display_get_name (display)), + G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS, + '-'); + user_name = g_strcanon (g_strdup (g_get_user_name ()), + G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS, + '-'); + + name = g_strdup_printf ("%s/%s-%s-%s", g_get_tmp_dir (), appname, user_name, + display_name[0] == '-' ? &display_name[1] : display_name); + + g_free (display_name); + g_free (user_name); + return name; +} + +static char * +get_pipe_path (const char *pipe_dir, + const char *name) +{ + return g_strdup_printf ("%s/" INPUT_PREFIX "%s", + pipe_dir, name); +} + +static gboolean +input_channel_start_io (int fd, + GIOFunc io_func, + gpointer data, + GIOChannel **io_channel, + guint *io_watch) +{ + GSource *source; + + *io_channel = g_io_channel_unix_new (fd); + g_io_channel_set_encoding (*io_channel, NULL, NULL); + + *io_watch = _moo_io_add_watch (*io_channel, + G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR, + io_func, data); + + source = g_main_context_find_source_by_id (NULL, *io_watch); + g_source_set_can_recurse (source, TRUE); + + return TRUE; +} + + +static gboolean do_send (const char *filename, + const char *data, + gssize data_len); + +static gboolean +try_send (const char *pipe_dir_name, + const char *name, + const char *data, + gssize data_len) +{ + char *filename = NULL; + gboolean result = FALSE; + + g_return_val_if_fail (name && name[0], FALSE); + + filename = g_strdup_printf ("%s/" INPUT_PREFIX "%s", pipe_dir_name, name); + _moo_message ("try_send: sending data to `%s'", filename); + + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) + { + _moo_message ("try_send: file %s doesn't exist", filename); + goto out; + } + + result = do_send (filename, data, data_len); + +out: + g_free (filename); + return result; +} + +gboolean +_moo_app_input_send_msg (const char *appname, + const char *name, + const char *data, + gssize len) +{ + const char *entry; + GDir *pipe_dir = NULL; + char *pipe_dir_name; + gboolean success = FALSE; + + g_return_val_if_fail (appname != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + _moo_message ("_moo_app_input_send_msg: sending data to %s", name ? name : "NONE"); + + pipe_dir_name = get_pipe_dir (appname); + g_return_val_if_fail (pipe_dir_name != NULL, FALSE); + + if (name) + { + success = try_send (pipe_dir_name, name, data, len); + goto out; + } + + pipe_dir = g_dir_open (pipe_dir_name, 0, NULL); + + if (!pipe_dir) + goto out; + + while ((entry = g_dir_read_name (pipe_dir))) + { + if (!strncmp (entry, INPUT_PREFIX, strlen (INPUT_PREFIX))) + { + name = entry + strlen (INPUT_PREFIX); + + if (try_send (pipe_dir_name, name, data, len)) + { + success = TRUE; + goto out; + } + } + } + +out: + if (pipe_dir) + g_dir_close (pipe_dir); + g_free (pipe_dir_name); + return success; +} + +static gboolean +do_write (int fd, + const char *data, + gsize data_len) +{ + while (data_len > 0) + { + ssize_t n; + + errno = 0; + n = write (fd, data, data_len); + + if (n < 0) + { + if (errno != EAGAIN && errno != EINTR) + { + g_warning ("%s in write: %s", G_STRLOC, g_strerror (errno)); + return FALSE; + } + } + else + { + data += n; + data_len -= n; + } + } + + return TRUE; +} + +#endif + + +#ifdef MOO_APP_INPUT_SOCKET + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +typedef struct { + int fd; + GIOChannel *io; + guint io_watch; + GString *buffer; /* messages are zero-terminated */ + InputChannel *ch; +} Connection; + +struct InputChannel +{ + char *name; + char *path; + char *pipe_dir; + gboolean owns_file; + int fd; + GIOChannel *io; + guint io_watch; + GSList *connections; +}; + +static void +connection_free (Connection *conn) +{ + if (conn->io_watch) + g_source_remove (conn->io_watch); + + if (conn->io) + { + g_io_channel_shutdown (conn->io, FALSE, NULL); + g_io_channel_unref (conn->io); + } + + if (conn->fd != -1) + close (conn->fd); + + g_string_free (conn->buffer, TRUE); + _moo_free (Connection, conn); +} + +static void +input_channel_shutdown (InputChannel *ch) +{ + g_slist_foreach (ch->connections, (GFunc) connection_free, NULL); + g_slist_free (ch->connections); + ch->connections = NULL; + + if (ch->io_watch) + { + g_source_remove (ch->io_watch); + ch->io_watch = 0; + } + + if (ch->io) + { + g_io_channel_shutdown (ch->io, FALSE, NULL); + g_io_channel_unref (ch->io); + ch->io = NULL; + } + + if (ch->fd != -1) + { + close (ch->fd); + ch->fd = -1; + } + + if (ch->path) + { + if (ch->owns_file) + unlink (ch->path); + g_free (ch->path); + ch->path = NULL; + } +} + +static gboolean +read_input (G_GNUC_UNUSED GIOChannel *source, + G_GNUC_UNUSED GIOCondition condition, + Connection *conn) +{ + char c; + int n; + + while ((n = read (conn->fd, &c, 1)) > 0) + { + if (c == 0) + break; + g_string_append_c (conn->buffer, c); + } + + if (n <= 0) + { + conn->ch->connections = g_slist_remove (conn->ch->connections, conn); + connection_free (conn); + return FALSE; + } + + commit (&conn->buffer); + return TRUE; +} + +static gboolean +accept_connection (G_GNUC_UNUSED GIOChannel *source, + GIOCondition condition, + InputChannel *ch) +{ + Connection *conn; + socklen_t dummy; + + if (condition & G_IO_ERR) + { + input_channel_shutdown (ch); + return FALSE; + } + + conn = _moo_new0 (Connection); + conn->ch = ch; + conn->buffer = g_string_new_len (NULL, MAX_BUFFER_SIZE); + + conn->fd = accept (ch->fd, NULL, &dummy); + + if (conn->fd == -1) + { + g_warning ("%s in accept: %s", G_STRLOC, g_strerror (errno)); + _moo_free (Connection, conn); + return TRUE; + } + + if (!input_channel_start_io (conn->fd, (GIOFunc) read_input, conn, + &conn->io, &conn->io_watch)) + { + close (conn->fd); + _moo_free (Connection, conn); + return TRUE; + } + + ch->connections = g_slist_prepend (ch->connections, conn); + return TRUE; +} + +static gboolean +input_channel_start (InputChannel *ch, + gboolean may_fail) +{ + struct sockaddr_un addr; + + mkdir (ch->pipe_dir, S_IRWXU); + + if (strlen (ch->path) + 1 > UNIX_PATH_MAX) + { + g_critical ("%s: oops", G_STRLOC); + return FALSE; + } + + addr.sun_family = AF_UNIX; + strncpy (addr.sun_path, ch->path, strlen (ch->path) + 1); + + errno = 0; + + if ((ch->fd = socket (PF_UNIX, SOCK_STREAM, 0)) == -1) + { + g_warning ("%s in socket for %s: %s", G_STRLOC, ch->path, g_strerror (errno)); + return FALSE; + } + + if (bind (ch->fd, (struct sockaddr*) &addr, sizeof addr) == -1) + _moo_message ("%s in bind for %s: %s", G_STRLOC, ch->path, g_strerror (errno)); + + if (listen (ch->fd, 5) == -1) + { + if (!may_fail) + g_warning ("%s in listen for %s: %s", G_STRLOC, ch->path, g_strerror (errno)); + close (ch->fd); + ch->fd = -1; + return FALSE; + } + + ch->owns_file = TRUE; + + if (!input_channel_start_io (ch->fd, (GIOFunc) accept_connection, ch, + &ch->io, &ch->io_watch)) + return FALSE; + + return TRUE; +} + +static InputChannel * +input_channel_new (const char *appname, + const char *name, + gboolean may_fail) +{ + InputChannel *ch; + + g_return_val_if_fail (appname != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + + ch = _moo_new0 (InputChannel); + + ch->name = g_strdup (name); + ch->pipe_dir = get_pipe_dir (appname); + ch->path = get_pipe_path (ch->pipe_dir, name); + ch->fd = -1; + ch->io = NULL; + ch->io_watch = 0; + + if (!input_channel_start (ch, may_fail)) + { + input_channel_free (ch); + return NULL; + } + + return ch; +} + +static void +input_channel_free (InputChannel *ch) +{ + input_channel_shutdown (ch); + g_free (ch->name); + g_free (ch->path); + + if (ch->pipe_dir) + { + remove (ch->pipe_dir); + g_free (ch->pipe_dir); + } + + _moo_free (InputChannel, ch); +} + + +static gboolean +do_send (const char *filename, + const char *data, + gssize data_len) +{ + struct sockaddr_un addr; + int fd; + char zero = 0; + gboolean result = TRUE; + + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (data != NULL || data_len == 0, FALSE); + + if (strlen (filename) + 1 > UNIX_PATH_MAX) + { + g_critical ("%s: oops", G_STRLOC); + return FALSE; + } + + addr.sun_family = AF_UNIX; + strncpy (addr.sun_path, filename, strlen (filename) + 1); + fd = socket (PF_UNIX, SOCK_STREAM, 0); + + if (fd == -1) + { + g_warning ("%s in socket for %s: %s", G_STRLOC, filename, g_strerror (errno)); + return FALSE; + } + + if (connect (fd, (struct sockaddr *) &addr, sizeof addr) == -1) + { + g_warning ("%s in connect for %s: %s", G_STRLOC, filename, g_strerror (errno)); + close (fd); + return FALSE; + } + + if (data_len < 0) + data_len = strlen (data); + + if (data_len) + result = do_write (fd, data, data_len); + if (result) + result = do_write (fd, &zero, 1); + + close (fd); + return result; +} + +#endif /* MOO_APP_INPUT_SOCKET */ + + +#ifdef MOO_APP_INPUT_PIPE + +struct InputChannel +{ + char *name; + char *path; + char *pipe_dir; + gboolean owns_file; + int fd; + GString *buffer; + GIOChannel *io; + guint io_watch; + GSList *connections; +}; + +static void +input_channel_shutdown (InputChannel *ch) +{ + if (ch->io_watch) + { + g_source_remove (ch->io_watch); + ch->io_watch = 0; + } + + if (ch->io) + { + g_io_channel_shutdown (ch->io, FALSE, NULL); + g_io_channel_unref (ch->io); + ch->io = NULL; + } + + if (ch->fd != -1) + { + close (ch->fd); + ch->fd = -1; + } + + if (ch->path) + { + unlink (ch->path); + g_free (ch->path); + ch->path = NULL; + } + + if (ch->buffer) + { + g_string_free (ch->buffer, TRUE); + ch->buffer = NULL; + } +} + +static gboolean +read_input (GIOChannel *source, + GIOCondition condition, + InputChannel *ch) +{ + gboolean error_occured = FALSE; + GError *err = NULL; + gboolean again = TRUE; + gboolean got_zero = FALSE; + + g_return_val_if_fail (source == ch->io, FALSE); + + /* XXX */ + if (condition & (G_IO_ERR | G_IO_HUP)) + error_occured = TRUE; + + while (again && !error_occured && !err) + { + char c; + int bytes_read; + + struct pollfd fd; + + fd.fd = ch->fd; + fd.events = POLLIN | POLLPRI; + fd.revents = 0; + + switch (poll (&fd, 1, 0)) + { + case -1: + if (errno != EINTR && errno != EAGAIN) + error_occured = TRUE; + perror ("poll"); + again = FALSE; + break; + + case 0: + if (0) + g_print ("%s: got nothing\n", G_STRLOC); + again = FALSE; + break; + + case 1: + if (fd.revents & (POLLERR)) + { + if (errno != EINTR && errno != EAGAIN) + error_occured = TRUE; + perror ("poll"); + } + else + { + bytes_read = read (ch->fd, &c, 1); + + if (bytes_read == 1) + { + g_string_append_c (ch->buffer, c); + + if (!c) + { + got_zero = TRUE; + again = FALSE; + } + } + else if (bytes_read == -1) + { + perror ("read"); + + if (errno != EINTR && errno != EAGAIN) + error_occured = TRUE; + + again = FALSE; + } + else + { + again = FALSE; + } + } + break; + + default: + g_assert_not_reached (); + } + } + + if (error_occured || err) + { + g_critical ("%s: %s", G_STRLOC, err ? err->message : "error"); + + if (err) + g_error_free (err); + + input_channel_shutdown (ch); + return FALSE; + } + + if (got_zero) + commit (&ch->buffer); + + return TRUE; +} + +static gboolean +input_channel_start (InputChannel *ch, + G_GNUC_UNUSED gboolean may_fail) +{ + mkdir (ch->pipe_dir, S_IRWXU); + unlink (ch->path); + + if (mkfifo (ch->path, S_IRUSR | S_IWUSR) != 0) + { + int err = errno; + g_critical ("%s: error in mkfifo()", G_STRLOC); + g_critical ("%s: %s", G_STRLOC, g_strerror (err)); + return FALSE; + } + + /* XXX O_RDWR is not good (man 3p open), but it must be opened for + * writing by us, otherwise we get POLLHUP when a writer dies on the other end. + * So, open for writing separately. */ + ch->fd = open (ch->path, O_RDWR | O_NONBLOCK); + if (ch->fd == -1) + { + int err = errno; + g_critical ("%s: error in open()", G_STRLOC); + g_critical ("%s: %s", G_STRLOC, g_strerror (err)); + return FALSE; + } + + if (0) + _moo_message ("%s: opened input pipe %s with fd %d", + G_STRLOC, ch->path, ch->fd); + + if (!input_channel_start_io (ch->fd, (GIOFunc) read_input, ch, + &ch->io, &ch->io_watch)) + return FALSE; + + return TRUE; +} + +static InputChannel * +input_channel_new (const char *appname, + const char *name, + gboolean may_fail) +{ + InputChannel *ch; + + g_return_val_if_fail (appname != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + + ch = _moo_new0 (InputChannel); + + ch->name = g_strdup (name); + ch->pipe_dir = get_pipe_dir (appname); + ch->path = get_pipe_path (ch->pipe_dir, name); + ch->fd = -1; + ch->io = NULL; + ch->io_watch = 0; + ch->buffer = g_string_new_len (NULL, MAX_BUFFER_SIZE); + + if (!input_channel_start (ch, may_fail)) + { + input_channel_free (ch); + return NULL; + } + + return ch; +} + +static void +input_channel_free (InputChannel *ch) +{ + input_channel_shutdown (ch); + + g_free (ch->name); + g_free (ch->path); + + if (ch->pipe_dir) + { + remove (ch->pipe_dir); + g_free (ch->pipe_dir); + } + + _moo_free (InputChannel, ch); +} + +static gboolean +do_send (const char *filename, + const char *data, + gssize data_len) +{ + gboolean result = FALSE; + int fd; + + _moo_message ("do_send: sending data to `%s'", filename); + + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) + { + _moo_message ("do_send: file `%s' doesn't exist", filename); + return FALSE; + } + + fd = open (filename, O_WRONLY | O_NONBLOCK); + + if (fd == -1) + { + _moo_message ("do_send: could not open `%s': %s", filename, g_strerror (errno)); + return FALSE; + } + + result = do_write (fd, data, data_len); + close (fd); + + if (result) + _moo_message ("do_send: successfully sent stuff to `%s'", filename); + + return result; +} + +#endif /* MOO_APP_INPUT_PIPE */ + +// MooAppInput * +// _moo_app_input_new (const char *pipe_basename) +// { +// MooAppInput *ch; +// +// g_return_val_if_fail (pipe_basename != NULL, NULL); +// +// ch = g_new0 (MooAppInput, 1); +// +// ch->pipe_basename = g_strdup (pipe_basename); +// +// #ifndef __WIN32__ +// ch->pipe = -1; +// #endif /* ! __WIN32__ */ +// ch->pipe_name = NULL; +// ch->io = NULL; +// ch->io_watch = 0; +// ch->ready = FALSE; +// ch->buffer = g_string_new_len (NULL, MAX_BUFFER_SIZE); +// +// return ch; +// } + + +// void +// _moo_app_input_shutdown (MooAppInput *ch) +// { +// g_return_if_fail (ch != NULL); +// +// #ifdef __WIN32__ +// if (ch->event_id) +// { +// _moo_event_queue_disconnect (ch->event_id); +// ch->event_id = 0; +// } +// #endif /* __WIN32__ */ +// +// if (ch->io) +// { +// g_io_channel_shutdown (ch->io, TRUE, NULL); +// g_io_channel_unref (ch->io); +// ch->io = NULL; +// } +// +// if (ch->pipe_name) +// { +// #ifndef __WIN32__ +// ch->pipe = -1; +// unlink (ch->pipe_name); +// #endif /* ! __WIN32__ */ +// g_free (ch->pipe_name); +// ch->pipe_name = NULL; +// } +// +// if (ch->pipe_dir) +// { +// remove (ch->pipe_dir); +// g_free (ch->pipe_dir); +// ch->pipe_dir = NULL; +// } +// +// if (ch->io_watch) +// { +// g_source_remove (ch->io_watch); +// ch->io_watch = 0; +// } +// +// ch->ready = FALSE; +// } + + /****************************************************************************/ /* WIN32 */ -#ifdef __WIN32__ - +#ifdef MOO_APP_INPUT_WIN32 typedef struct { char *pipe_name; guint event_id; } ListenerInfo; +struct InputChannel +{ + char *appname; + char *name; + char *pipe_name; + GString *buffer; + guint event_id; +}; + static ListenerInfo * listener_info_new (const char *pipe_name, guint event_id) @@ -292,31 +1067,30 @@ listener_main (ListenerInfo *info) static char * -get_pipe_name (const char *pipe_basename, - const char *pid_string) +get_pipe_name (const char *appname, + const char *name) { - if (!pid_string) - pid_string = _moo_get_pid_string (); - + if (!name) + name = _moo_get_pid_string (); return g_strdup_printf ("\\\\.\\pipe\\%s_in_%s", - pipe_basename, pid_string); + appname, name); } static void -event_callback (GList *events, - MooAppInput *chan) +event_callback (GList *events, + InputChannel *ch) { while (events) { char c = GPOINTER_TO_INT (events->data); if (c != 0) - g_string_append_c (chan->buffer, c); + g_string_append_c (ch->buffer, c); else { gdk_threads_enter (); - commit (chan); + commit (&ch->buffer); gdk_threads_leave (); } @@ -325,15 +1099,13 @@ event_callback (GList *events, } -gboolean -_moo_app_input_start (MooAppInput *ch) +static gboolean +input_channel_start (InputChannel *ch) { ListenerInfo *info; - g_return_val_if_fail (ch != NULL && !ch->ready, FALSE); - g_free (ch->pipe_name); - ch->pipe_name = get_pipe_name (ch->pipe_basename, NULL); + ch->pipe_name = get_pipe_name (ch->appname, ch->name); ch->event_id = _moo_event_queue_connect ((MooEventQueueCallback) event_callback, ch, NULL); @@ -350,14 +1122,47 @@ _moo_app_input_start (MooAppInput *ch) return FALSE; } - ch->ready = TRUE; return TRUE; } +static InputChannel * +input_channel_new (const char *appname, + const char *name, + G_GNUC_UNUSED gboolean may_fail) +{ + InputChannel *ch; + + ch = _moo_new0 (InputChannel); + ch->appname = g_strdup (appname); + ch->name = g_strdup (name); + ch->buffer = g_string_new_len (NULL, MAX_BUFFER_SIZE); + + if (!input_channel_start (ch)) + { + input_channel_free (ch); + return NULL; + } + + return ch; +} + +static void +input_channel_free (InputChannel *ch) +{ + if (ch->event_id) + _moo_event_queue_disconnect (ch->event_id); + if (ch->buffer) + g_string_free (ch->buffer, TRUE); + g_free (ch->pipe_name); + g_free (ch->appname); + g_free (ch->name); + _moo_free (InputChannel, ch); +} + gboolean -_moo_app_input_send_msg (const char *pipe_basename, - const char *pid, +_moo_app_input_send_msg (const char *appname, + const char *name, const char *data, gssize len) { @@ -367,7 +1172,7 @@ _moo_app_input_send_msg (const char *pipe_basename, gboolean result = FALSE; DWORD bytes_written; - g_return_val_if_fail (pipe_basename != NULL, FALSE); + g_return_val_if_fail (appname != NULL, FALSE); g_return_val_if_fail (data != NULL, FALSE); if (len < 0) @@ -376,10 +1181,10 @@ _moo_app_input_send_msg (const char *pipe_basename, if (!len) return TRUE; - if (!pid) - return FALSE; + if (!name) + name = "main"; - pipe_name = get_pipe_name (pipe_basename, pid); + pipe_name = get_pipe_name (appname, name); pipe_handle = CreateFile (pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (!pipe_handle) @@ -389,7 +1194,7 @@ _moo_app_input_send_msg (const char *pipe_basename, goto out; } - if (!WriteFile(pipe_handle, data, len, &bytes_written, NULL)) + if (!WriteFile (pipe_handle, data, len, &bytes_written, NULL)) { err_msg = g_win32_error_message (GetLastError ()); g_warning ("could not write data to '%s': %s", pipe_name, err_msg); @@ -413,339 +1218,371 @@ out: return result; } -#endif /* __WIN32__ */ - - -/****************************************************************************/ -/* UNIX - */ -#ifndef __WIN32__ - -#define INPUT_PREFIX "input-" - -static gboolean -read_input (GIOChannel *source, - GIOCondition condition, - MooAppInput *self) -{ - gboolean error_occured = FALSE; - GError *err = NULL; - gboolean again = TRUE; - gboolean got_zero = FALSE; - - g_return_val_if_fail (source == self->io, FALSE); - - /* XXX */ - if (condition & (G_IO_ERR | G_IO_HUP)) - error_occured = TRUE; - - while (again && !error_occured && !err) - { - char c; - int bytes_read; - - struct pollfd fd; - - fd.fd = self->pipe; - fd.events = POLLIN | POLLPRI; - fd.revents = 0; - - switch (poll (&fd, 1, 0)) - { - case -1: - if (errno != EINTR && errno != EAGAIN) - error_occured = TRUE; - perror ("poll"); - again = FALSE; - break; - - case 0: - if (0) - g_print ("%s: got nothing\n", G_STRLOC); - again = FALSE; - break; - - case 1: - if (fd.revents & (POLLERR)) - { - if (errno != EINTR && errno != EAGAIN) - error_occured = TRUE; - perror ("poll"); - } - else - { - bytes_read = read (self->pipe, &c, 1); - - if (bytes_read == 1) - { - g_string_append_c (self->buffer, c); - - if (!c) - { - got_zero = TRUE; - again = FALSE; - } - } - else if (bytes_read == -1) - { - perror ("read"); - - if (errno != EINTR && errno != EAGAIN) - error_occured = TRUE; - - again = FALSE; - } - else - { - again = FALSE; - } - } - break; - - default: - g_assert_not_reached (); - } - } - - if (error_occured || err) - { - g_critical ("%s: %s", G_STRLOC, err ? err->message : "error"); - - if (err) - g_error_free (err); - - _moo_app_input_shutdown (self); - return FALSE; - } - - if (got_zero) - commit (self); - - return TRUE; -} - - -static char * -get_pipe_dir (const char *pipe_basename) -{ - GdkDisplay *display; - char *display_name; - char *user_name; - char *name; - - g_return_val_if_fail (pipe_basename != NULL, NULL); - - display = gdk_display_get_default (); - g_return_val_if_fail (display != NULL, NULL); - - display_name = g_strcanon (g_strdup (gdk_display_get_name (display)), - G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS, - '-'); - user_name = g_strcanon (g_strdup (g_get_user_name ()), - G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS, - '-'); - - name = g_strdup_printf ("%s/%s-%s-%s", g_get_tmp_dir (), pipe_basename, user_name, - display_name[0] == '-' ? &display_name[1] : display_name); - - g_free (display_name); - g_free (user_name); - return name; -} - -/* TODO: could you finally learn non-blocking io? */ -gboolean -_moo_app_input_start (MooAppInput *ch) -{ - GSource *source; - - g_return_val_if_fail (!ch->ready, FALSE); - - if (!ch->pipe_dir) - ch->pipe_dir = get_pipe_dir (ch->pipe_basename); - g_return_val_if_fail (ch->pipe_dir != NULL, FALSE); - - mkdir (ch->pipe_dir, S_IRWXU); - - ch->pipe_name = g_strdup_printf ("%s/"INPUT_PREFIX"%d", - ch->pipe_dir, - getpid ()); - - unlink (ch->pipe_name); - - if (mkfifo (ch->pipe_name, S_IRUSR | S_IWUSR)) - { - int err = errno; - g_critical ("%s: error in mkfifo()", G_STRLOC); - g_critical ("%s: %s", G_STRLOC, g_strerror (err)); - return FALSE; - } - - /* XXX O_RDWR is not good (man 3p open), but it must be opened for - * writing by us, otherwise we get POLLHUP when a writer dies on the other end. - * So, open for writing separately. */ - ch->pipe = open (ch->pipe_name, O_RDWR | O_NONBLOCK); - if (ch->pipe == -1) - { - int err = errno; - g_critical ("%s: error in open()", G_STRLOC); - g_critical ("%s: %s", G_STRLOC, g_strerror (err)); - g_free (ch->pipe_name); - ch->pipe_name = NULL; - return FALSE; - } - - if (0) - _moo_message ("%s: opened input pipe %s with fd %d", - G_STRLOC, ch->pipe_name, ch->pipe); - - ch->io = g_io_channel_unix_new (ch->pipe); - g_io_channel_set_encoding (ch->io, NULL, NULL); - ch->io_watch = _moo_io_add_watch (ch->io, - G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR, - (GIOFunc) read_input, - ch); - - source = g_main_context_find_source_by_id (NULL, ch->io_watch); - g_source_set_can_recurse (source, TRUE); - - ch->ready = TRUE; - return TRUE; -} - - -static gboolean -try_send (const char *pipe_dir_name, - const char *pid_string, - const char *data, - gssize data_len) -{ - GPid pid; - char *filename = NULL; - GIOChannel *chan = NULL; - GIOStatus status; - gboolean result = FALSE; - char *endptr; - GError *error = NULL; - - if (!pid_string[0]) - goto out; - - errno = 0; - pid = strtol (pid_string, &endptr, 10); - - if (errno != 0 || endptr == pid_string || *endptr != 0) - { - g_warning ("invalid pid string '%s'", pid_string); - goto out; - } - - filename = g_strdup_printf ("%s/"INPUT_PREFIX"%s", pipe_dir_name, pid_string); - _moo_message ("try_send: sending data to pid %s, filename %s", pid_string, filename); - - if (!g_file_test (filename, G_FILE_TEST_EXISTS)) - { - _moo_message ("try_send: file %s doesn't exist", filename); - goto out; - } - - if (kill (pid, 0) != 0) - { - _moo_message ("try_send: no process with pid %d", pid); - unlink (filename); - goto out; - } - - chan = g_io_channel_new_file (filename, "w", &error); - - if (!chan) - { - _moo_message ("try_send: could not open %s for writing: %s", - filename, error ? error->message : ""); - g_error_free (error); - goto out; - } - - g_io_channel_set_encoding (chan, NULL, NULL); - status = g_io_channel_set_flags (chan, G_IO_FLAG_NONBLOCK, &error); - - if (status != G_IO_STATUS_NORMAL) - { - _moo_message ("try_send: could not set NONBLOCK flag: %s", - error ? error->message : ""); - goto out; - } - - status = g_io_channel_write_chars (chan, data, data_len, NULL, &error); - - if (status != G_IO_STATUS_NORMAL) - { - _moo_message ("try_send: error writing to pipe: %s", - error ? error->message : ""); - goto out; - } - - result = TRUE; - _moo_message ("try_send: successfully sent stuff to pid %s", pid_string); - -out: - if (chan) - g_io_channel_unref (chan); - if (error) - g_error_free (error); - g_free (filename); - return result; -} - -gboolean -_moo_app_input_send_msg (const char *pipe_basename, - const char *pid, - const char *data, - gssize data_len) -{ - const char *entry; - GDir *pipe_dir = NULL; - char *pipe_dir_name; - gboolean success = FALSE; - - g_return_val_if_fail (pipe_basename != NULL, FALSE); - g_return_val_if_fail (data != NULL, FALSE); - - _moo_message ("_moo_app_input_send_msg: sending data to pid %s", pid ? pid : "NONE"); - - pipe_dir_name = get_pipe_dir (pipe_basename); - g_return_val_if_fail (pipe_dir_name != NULL, FALSE); - - if (pid) - { - success = try_send (pipe_dir_name, pid, data, data_len); - goto out; - } - - pipe_dir = g_dir_open (pipe_dir_name, 0, NULL); - - if (!pipe_dir) - goto out; - - while ((entry = g_dir_read_name (pipe_dir))) - { - if (!strncmp (entry, INPUT_PREFIX, strlen (INPUT_PREFIX))) - { - const char *pid_string = entry + strlen (INPUT_PREFIX); - - if (try_send (pipe_dir_name, pid_string, data, data_len)) - { - success = TRUE; - goto out; - } - } - } - -out: - if (pipe_dir) - g_dir_close (pipe_dir); - g_free (pipe_dir_name); - return success; -} - -#endif /* ! __WIN32__ */ +#endif /* MOO_APP_INPUT_WIN32 */ + +// gboolean +// _moo_app_input_send_msg (const char *pipe_basename, +// const char *pid, +// const char *data, +// gssize len) +// { +// char *err_msg = NULL; +// char *pipe_name; +// HANDLE pipe_handle; +// gboolean result = FALSE; +// DWORD bytes_written; +// +// g_return_val_if_fail (pipe_basename != NULL, FALSE); +// g_return_val_if_fail (data != NULL, FALSE); +// +// if (len < 0) +// len = strlen (data); +// +// if (!len) +// return TRUE; +// +// if (!pid) +// return FALSE; +// +// pipe_name = get_pipe_name (pipe_basename, pid); +// pipe_handle = CreateFile (pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); +// +// if (!pipe_handle) +// { +// err_msg = g_win32_error_message (GetLastError ()); +// g_warning ("could not open pipe '%s': %s", pipe_name, err_msg); +// goto out; +// } +// +// if (!WriteFile(pipe_handle, data, len, &bytes_written, NULL)) +// { +// err_msg = g_win32_error_message (GetLastError ()); +// g_warning ("could not write data to '%s': %s", pipe_name, err_msg); +// goto out; +// } +// +// if (bytes_written < (DWORD) len) +// { +// g_warning ("written less data than requested to '%s'", pipe_name); +// goto out; +// } +// +// result = TRUE; +// +// out: +// if (pipe_handle != INVALID_HANDLE_VALUE) +// CloseHandle (pipe_handle); +// +// g_free (pipe_name); +// g_free (err_msg); +// return result; +// } +// +// #endif /* __WIN32__ */ +// +// +// /****************************************************************************/ +// /* UNIX +// */ +// #ifndef __WIN32__ +// +// #define INPUT_PREFIX "input-" +// +// static gboolean +// read_input (GIOChannel *source, +// GIOCondition condition, +// MooAppInput *self) +// { +// gboolean error_occured = FALSE; +// GError *err = NULL; +// gboolean again = TRUE; +// gboolean got_zero = FALSE; +// +// g_return_val_if_fail (source == self->io, FALSE); +// +// /* XXX */ +// if (condition & (G_IO_ERR | G_IO_HUP)) +// error_occured = TRUE; +// +// while (again && !error_occured && !err) +// { +// char c; +// int bytes_read; +// +// struct pollfd fd; +// +// fd.fd = self->pipe; +// fd.events = POLLIN | POLLPRI; +// fd.revents = 0; +// +// switch (poll (&fd, 1, 0)) +// { +// case -1: +// if (errno != EINTR && errno != EAGAIN) +// error_occured = TRUE; +// perror ("poll"); +// again = FALSE; +// break; +// +// case 0: +// if (0) +// g_print ("%s: got nothing\n", G_STRLOC); +// again = FALSE; +// break; +// +// case 1: +// if (fd.revents & (POLLERR)) +// { +// if (errno != EINTR && errno != EAGAIN) +// error_occured = TRUE; +// perror ("poll"); +// } +// else +// { +// bytes_read = read (self->pipe, &c, 1); +// +// if (bytes_read == 1) +// { +// g_string_append_c (self->buffer, c); +// +// if (!c) +// { +// got_zero = TRUE; +// again = FALSE; +// } +// } +// else if (bytes_read == -1) +// { +// perror ("read"); +// +// if (errno != EINTR && errno != EAGAIN) +// error_occured = TRUE; +// +// again = FALSE; +// } +// else +// { +// again = FALSE; +// } +// } +// break; +// +// default: +// g_assert_not_reached (); +// } +// } +// +// if (error_occured || err) +// { +// g_critical ("%s: %s", G_STRLOC, err ? err->message : "error"); +// +// if (err) +// g_error_free (err); +// +// _moo_app_input_shutdown (self); +// return FALSE; +// } +// +// if (got_zero) +// commit (self); +// +// return TRUE; +// } +// +// +// /* TODO: could you finally learn non-blocking io? */ +// gboolean +// _moo_app_input_start (MooAppInput *ch) +// { +// GSource *source; +// +// g_return_val_if_fail (!ch->ready, FALSE); +// +// if (!ch->pipe_dir) +// ch->pipe_dir = get_pipe_dir (ch->pipe_basename); +// g_return_val_if_fail (ch->pipe_dir != NULL, FALSE); +// +// mkdir (ch->pipe_dir, S_IRWXU); +// +// ch->pipe_name = g_strdup_printf ("%s/"INPUT_PREFIX"%d", +// ch->pipe_dir, +// getpid ()); +// +// unlink (ch->pipe_name); +// +// if (mkfifo (ch->pipe_name, S_IRUSR | S_IWUSR)) +// { +// int err = errno; +// g_critical ("%s: error in mkfifo()", G_STRLOC); +// g_critical ("%s: %s", G_STRLOC, g_strerror (err)); +// return FALSE; +// } +// +// /* XXX O_RDWR is not good (man 3p open), but it must be opened for +// * writing by us, otherwise we get POLLHUP when a writer dies on the other end. +// * So, open for writing separately. */ +// ch->pipe = open (ch->pipe_name, O_RDWR | O_NONBLOCK); +// if (ch->pipe == -1) +// { +// int err = errno; +// g_critical ("%s: error in open()", G_STRLOC); +// g_critical ("%s: %s", G_STRLOC, g_strerror (err)); +// g_free (ch->pipe_name); +// ch->pipe_name = NULL; +// return FALSE; +// } +// +// if (0) +// _moo_message ("%s: opened input pipe %s with fd %d", +// G_STRLOC, ch->pipe_name, ch->pipe); +// +// ch->io = g_io_channel_unix_new (ch->pipe); +// g_io_channel_set_encoding (ch->io, NULL, NULL); +// ch->io_watch = _moo_io_add_watch (ch->io, +// G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR, +// (GIOFunc) read_input, +// ch); +// +// source = g_main_context_find_source_by_id (NULL, ch->io_watch); +// g_source_set_can_recurse (source, TRUE); +// +// ch->ready = TRUE; +// return TRUE; +// } +// +// +// static gboolean +// try_send (const char *pipe_dir_name, +// const char *pid_string, +// const char *data, +// gssize data_len) +// { +// GPid pid; +// char *filename = NULL; +// GIOChannel *chan = NULL; +// GIOStatus status; +// gboolean result = FALSE; +// char *endptr; +// GError *error = NULL; +// +// if (!pid_string[0]) +// goto out; +// +// errno = 0; +// pid = strtol (pid_string, &endptr, 10); +// +// if (errno != 0 || endptr == pid_string || *endptr != 0) +// { +// g_warning ("invalid pid string '%s'", pid_string); +// goto out; +// } +// +// filename = g_strdup_printf ("%s/"INPUT_PREFIX"%s", pipe_dir_name, pid_string); +// _moo_message ("try_send: sending data to pid %s, filename %s", pid_string, filename); +// +// if (!g_file_test (filename, G_FILE_TEST_EXISTS)) +// { +// _moo_message ("try_send: file %s doesn't exist", filename); +// goto out; +// } +// +// if (kill (pid, 0) != 0) +// { +// _moo_message ("try_send: no process with pid %d", pid); +// unlink (filename); +// goto out; +// } +// +// chan = g_io_channel_new_file (filename, "w", &error); +// +// if (!chan) +// { +// _moo_message ("try_send: could not open %s for writing: %s", +// filename, error ? error->message : ""); +// g_error_free (error); +// goto out; +// } +// +// g_io_channel_set_encoding (chan, NULL, NULL); +// status = g_io_channel_set_flags (chan, G_IO_FLAG_NONBLOCK, &error); +// +// if (status != G_IO_STATUS_NORMAL) +// { +// _moo_message ("try_send: could not set NONBLOCK flag: %s", +// error ? error->message : ""); +// goto out; +// } +// +// status = g_io_channel_write_chars (chan, data, data_len, NULL, &error); +// +// if (status != G_IO_STATUS_NORMAL) +// { +// _moo_message ("try_send: error writing to pipe: %s", +// error ? error->message : ""); +// goto out; +// } +// +// result = TRUE; +// _moo_message ("try_send: successfully sent stuff to pid %s", pid_string); +// +// out: +// if (chan) +// g_io_channel_unref (chan); +// if (error) +// g_error_free (error); +// g_free (filename); +// return result; +// } +// +// gboolean +// _moo_app_input_send_msg (const char *pipe_basename, +// const char *pid, +// const char *data, +// gssize data_len) +// { +// const char *entry; +// GDir *pipe_dir = NULL; +// char *pipe_dir_name; +// gboolean success = FALSE; +// +// g_return_val_if_fail (pipe_basename != NULL, FALSE); +// g_return_val_if_fail (data != NULL, FALSE); +// +// _moo_message ("_moo_app_input_send_msg: sending data to pid %s", pid ? pid : "NONE"); +// +// pipe_dir_name = get_pipe_dir (pipe_basename); +// g_return_val_if_fail (pipe_dir_name != NULL, FALSE); +// +// if (pid) +// { +// success = try_send (pipe_dir_name, pid, data, data_len); +// goto out; +// } +// +// pipe_dir = g_dir_open (pipe_dir_name, 0, NULL); +// +// if (!pipe_dir) +// goto out; +// +// while ((entry = g_dir_read_name (pipe_dir))) +// { +// if (!strncmp (entry, INPUT_PREFIX, strlen (INPUT_PREFIX))) +// { +// const char *pid_string = entry + strlen (INPUT_PREFIX); +// +// if (try_send (pipe_dir_name, pid_string, data, data_len)) +// { +// success = TRUE; +// goto out; +// } +// } +// } +// +// out: +// if (pipe_dir) +// g_dir_close (pipe_dir); +// g_free (pipe_dir_name); +// return success; +// } +// +// #endif /* ! __WIN32__ */ diff --git a/moo/mooapp/mooappinput.h b/moo/mooapp/mooappinput.h index fbc97146..8c7825c6 100644 --- a/moo/mooapp/mooappinput.h +++ b/moo/mooapp/mooappinput.h @@ -65,21 +65,18 @@ static const char *moo_app_cmd_chars = #endif /* WANT_MOO_APP_CMD_CHARS */ +#define MOO_APP_INPUT_NAME_DEFAULT "main" typedef struct _MooAppInput MooAppInput; -MooAppInput *_moo_app_input_new (const char *pipe_basename); - +MooAppInput *_moo_app_input_new (const char *appname, + const char *name, + gboolean bind_default); void _moo_app_input_free (MooAppInput *ch); -gboolean _moo_app_input_start (MooAppInput *ch); -void _moo_app_input_shutdown (MooAppInput *ch); - -const char *_moo_app_input_get_name (MooAppInput *ch); - -gboolean _moo_app_input_send_msg (const char *pipe_basename, - const char *pid, +gboolean _moo_app_input_send_msg (const char *appname, + const char *name, const char *data, gssize len); diff --git a/moo/moopython/pygtk/mooapp-pygtk.defs b/moo/moopython/pygtk/mooapp-pygtk.defs index 01da1246..58dc28e1 100644 --- a/moo/moopython/pygtk/mooapp-pygtk.defs +++ b/moo/moopython/pygtk/mooapp-pygtk.defs @@ -64,12 +64,6 @@ ;; (return-type "const-MooAppInfo*") ;; ) -(define-method get_input_pipe_name - (of-object "MooApp") - (c-name "moo_app_get_input_pipe_name") - (return-type "const-char*") -) - (define-method get_editor (of-object "MooApp") (c-name "moo_app_get_editor")