Support sending multiple actions to a running instance

Currently we support only one --command action arg
type of option. Extend it to allow multiple such commands
in one invocation.

Trac #498

Signed-off-by: Selva Nair <selva.nair@gmail.com>
pull/502/head
Selva Nair 2022-05-23 14:42:53 -04:00
parent 79f5cb91c6
commit 6787306b16
3 changed files with 91 additions and 56 deletions

72
main.c
View File

@ -100,7 +100,7 @@ VerifyAutoConnections()
* to the running instance and return success or error. * to the running instance and return success or error.
*/ */
static int static int
NotifyRunningInstance() NotifyRunningInstance(int action_type, wchar_t *action_arg)
{ {
/* Check if a previous instance has a window initialized /* Check if a previous instance has a window initialized
* Even if we are not the first instance this may return null * Even if we are not the first instance this may return null
@ -110,22 +110,16 @@ NotifyRunningInstance()
int exit_code = 0; int exit_code = 0;
if (hwnd_master) if (hwnd_master)
{ {
/* GUI up and running -- send a message if any action is pecified, /* GUI up and running -- send a message for the specified action */
else show the balloon */
COPYDATASTRUCT config_data = {0}; COPYDATASTRUCT config_data = {0};
int timeout = 30*1000; /* 30 seconds */ int timeout = 30*1000; /* 30 seconds */
if (!o.action) config_data.dwData = action_type;
if (action_arg)
{ {
o.action = WM_OVPN_NOTIFY; config_data.cbData = (wcslen(action_arg)+1)*sizeof(action_arg[0]);
o.action_arg = LoadLocalizedString(IDS_NFO_CLICK_HERE_TO_START); config_data.lpData = (void *) action_arg;
} }
config_data.dwData = o.action; PrintDebug(L"Instance 2: called with action %d : %ls", action_type, action_arg);
if (o.action_arg)
{
config_data.cbData = (wcslen(o.action_arg)+1)*sizeof(o.action_arg[0]);
config_data.lpData = (void *) o.action_arg;
}
PrintDebug(L"Instance 2: called with action %d : %ls", o.action, o.action_arg);
if (!SendMessageTimeout (hwnd_master, WM_COPYDATA, 0, if (!SendMessageTimeout (hwnd_master, WM_COPYDATA, 0,
(LPARAM) &config_data, 0, timeout, NULL)) (LPARAM) &config_data, 0, timeout, NULL))
{ {
@ -245,23 +239,40 @@ int WINAPI _tWinMain (HINSTANCE hThisInstance,
if (!first_instance) if (!first_instance)
{ {
int res = NotifyRunningInstance(); int exit_code = 0;
exit(res); struct action *a = o.action_list.head;
if (!a) /* no actions -- send a balloon notification */
{
exit_code = NotifyRunningInstance(WM_OVPN_NOTIFY,
LoadLocalizedString(IDS_NFO_CLICK_HERE_TO_START));
}
else while (a)
{
int res = NotifyRunningInstance(a->type, a->arg);
exit_code = res > exit_code ? res : exit_code;
a = a->next;
}
exit(exit_code);
} }
else if (o.action == WM_OVPN_START) else
{ {
PrintDebug(L"Instance 1: Called with --command connect xxx. Treating it as --connect xxx"); for (struct action *a = o.action_list.head; a; a = a->next)
{
if (a->type == WM_OVPN_START)
{
PrintDebug(L"Instance 1: Called with --command connect xxx. Treating it as --connect xxx");
}
else if (a->type == WM_OVPN_IMPORT)
{
; /* pass -- import is handled after Window initialization */
}
else
{
MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Called with --command when no previous instance available (action type = %d arg = %s", a->type, a->arg ? a->arg : L"");
exit(OVPN_EXITCODE_ERROR);
}
}
} }
else if (o.action == WM_OVPN_IMPORT)
{
; /* pass -- import is handled after Window initialization */
}
else if (o.action)
{
MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Called with --command when no previous instance available");
exit(OVPN_EXITCODE_ERROR);
}
if (!CheckVersion()) { if (!CheckVersion()) {
exit(1); exit(1);
} }
@ -528,9 +539,12 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM
CheckServiceStatus(); // Check if service is running or not CheckServiceStatus(); // Check if service is running or not
/* if '--import' was specified, do it now */ /* if '--import' was specified, do it now */
if (o.action == WM_OVPN_IMPORT && o.action_arg) for (struct action *a = o.action_list.head; a ; a = a->next)
{ {
ImportConfigFile(o.action_arg, true); /* prompt user */ if (a->type == WM_OVPN_IMPORT && a->arg)
{
ImportConfigFile(a->arg, true); /* prompt user */
}
} }
if (!AutoStartConnections()) { if (!AutoStartConnections()) {

View File

@ -36,6 +36,7 @@
#include <shlobj.h> #include <shlobj.h>
#include <shlwapi.h> #include <shlwapi.h>
#include <combaseapi.h> #include <combaseapi.h>
#include <assert.h>
#include "options.h" #include "options.h"
#include "main.h" #include "main.h"
@ -81,9 +82,32 @@ ExpandOptions (void)
ExpandString (o.install_path, _countof(o.install_path)); ExpandString (o.install_path, _countof(o.install_path));
} }
static void
add_action(struct action_list *al, DWORD type, wchar_t *arg)
{
struct action *a = calloc(sizeof(*a), 1);
if (!a)
{
ErrorExit(1, L"Out of memory while parsing command line");
}
a->type = type;
a->arg = arg;
if (!al->head) /* first entry */
{
al->head = a;
}
else
{
assert(al->tail);
al->tail->next = a;
}
al->tail = a;
}
static int static int
add_option(options_t *options, int i, TCHAR **p) add_option(options_t *options, int i, TCHAR **p)
{ {
struct action_list *al = &options->action_list;
if (streq(p[0], _T("help"))) if (streq(p[0], _T("help")))
{ {
TCHAR caption[200]; TCHAR caption[200];
@ -112,14 +136,10 @@ add_option(options_t *options, int i, TCHAR **p)
options->auto_connect = tmp; options->auto_connect = tmp;
} }
options->auto_connect[options->num_auto_connect++] = p[1]; options->auto_connect[options->num_auto_connect++] = p[1];
/* Treat the first connect option to also mean --command connect profile. /* Treat connect option to also mean --command connect profile.
* This gets used if we are not the first instance. * This gets used if we are not the first instance.
*/ */
if (options->num_auto_connect == 1) add_action(al, WM_OVPN_START, p[1]);
{
options->action = WM_OVPN_START;
options->action_arg = p[1];
}
} }
else if (streq(p[0], L"import") && p[1]) else if (streq(p[0], L"import") && p[1])
{ {
@ -127,8 +147,7 @@ add_option(options_t *options, int i, TCHAR **p)
/* This is interpreted directly or as a command depending /* This is interpreted directly or as a command depending
* on we are the first instance or not. So, set as an action. * on we are the first instance or not. So, set as an action.
*/ */
options->action = WM_OVPN_IMPORT; add_action(al, WM_OVPN_IMPORT, p[1]);
options->action_arg = p[1];
} }
else if (streq(p[0], _T("exe_path")) && p[1]) else if (streq(p[0], _T("exe_path")) && p[1])
{ {
@ -248,52 +267,44 @@ add_option(options_t *options, int i, TCHAR **p)
if (streq(p[1], _T("connect")) && p[2]) if (streq(p[1], _T("connect")) && p[2])
{ {
/* Treat this as "--connect profile" in case this is the first instance */ /* Treat this as "--connect profile" in case this is the first instance */
add_option(options, i, &p[1]); i = add_option(options, i, &p[1]);
++i;
options->action = WM_OVPN_START;
options->action_arg = p[2];
} }
else if (streq(p[1], _T("disconnect")) && p[2]) else if (streq(p[1], _T("disconnect")) && p[2])
{ {
++i; ++i;
options->action = WM_OVPN_STOP; add_action(al, WM_OVPN_STOP, p[2]);
options->action_arg = p[2];
} }
else if (streq(p[1], _T("reconnect")) && p[2]) else if (streq(p[1], _T("reconnect")) && p[2])
{ {
++i; ++i;
options->action = WM_OVPN_RESTART; add_action(al, WM_OVPN_RESTART, p[2]);
options->action_arg = p[2];
} }
else if (streq(p[1], _T("status")) && p[2]) else if (streq(p[1], _T("status")) && p[2])
{ {
++i; ++i;
options->action = WM_OVPN_SHOWSTATUS; add_action(al, WM_OVPN_SHOWSTATUS, p[2]);
options->action_arg = p[2];
} }
else if (streq(p[1], L"import") && p[2]) else if (streq(p[1], L"import") && p[2])
{ {
++i; ++i;
options->action = WM_OVPN_IMPORT; add_action(al, WM_OVPN_IMPORT, p[2]);
options->action_arg = p[2];
} }
else if (streq(p[1], _T("silent_connection"))) else if (streq(p[1], _T("silent_connection")))
{ {
++i; ++i;
options->action = WM_OVPN_SILENT; add_action(al, WM_OVPN_SILENT, p[2] ? p[2] : L"1");
options->action_arg = p[2] ? p[2] : _T("1");
} }
else if (streq(p[1], _T("disconnect_all"))) else if (streq(p[1], _T("disconnect_all")))
{ {
options->action = WM_OVPN_STOPALL; add_action(al, WM_OVPN_STOPALL, NULL);
} }
else if (streq(p[1], _T("exit"))) else if (streq(p[1], _T("exit")))
{ {
options->action = WM_OVPN_EXIT; add_action(al, WM_OVPN_EXIT, NULL);
} }
else if (streq(p[1], _T("rescan"))) else if (streq(p[1], _T("rescan")))
{ {
options->action = WM_OVPN_RESCAN; add_action(al, WM_OVPN_RESCAN, NULL);
} }
else else
{ {

View File

@ -158,6 +158,17 @@ struct connection {
struct echo_msg echo_msg; /* Message echo-ed from server or client config and related data */ struct echo_msg echo_msg; /* Message echo-ed from server or client config and related data */
}; };
/* Command actions to be send to running instance */
struct action {
int type;
wchar_t *arg;
struct action *next;
};
struct action_list {
struct action *head, *tail;
};
/* All options used within OpenVPN GUI */ /* All options used within OpenVPN GUI */
typedef struct { typedef struct {
/* Array of configs to autostart */ /* Array of configs to autostart */
@ -223,8 +234,7 @@ typedef struct {
unsigned int dpi_scale; unsigned int dpi_scale;
COLORREF clr_warning; COLORREF clr_warning;
COLORREF clr_error; COLORREF clr_error;
int action; /* action to send to a running instance */ struct action_list action_list; /* list of actions to send to a running instance */
TCHAR *action_arg;
HANDLE session_semaphore; HANDLE session_semaphore;
HANDLE event_log; HANDLE event_log;
} options_t; } options_t;