source: branches/help/SL/MACROS/xcmd.cxx

Last change on this file was 19552, checked in by westram, 2 days ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 7.0 KB
Line 
1// ========================================================= //
2//                                                           //
3//   File      : xcmd.cxx                                    //
4//   Purpose   : support to run commands in (x)terminal      //
5//                                                           //
6//   Coded by Ralf Westram (coder@reallysoft.de) in Sep 25   //
7//   http://www.arb-home.de/                                 //
8//                                                           //
9// ========================================================= //
10
11#include "xcmd.hxx"
12#include "dbserver.hxx"
13
14#include <arbdb.h>
15#include <arb_strbuf.h>
16#include <arb_sleep.h>
17#include <aw_msg.hxx>
18
19using namespace std;
20
21// ---------------------------
22//      external commands
23
24#define SYNC_STATE_STARTED  "started"
25#define SYNC_STATE_FINISHED "finished"
26
27static GB_ERROR wait_for_sync__servingDB(GBDATA *gb_main, const char *sync_id) {
28    bool     sync_reached = false;
29    GB_ERROR error        = NULp;
30
31    ARB_timeout show_wait(2, SEC);
32    ARB_timeout show_serve_error(500, MS);
33
34    while (!sync_reached && !error) {
35        GB_ERROR serve_error = serve_db_while_GUI_is_blocked(gb_main);
36        if (serve_error) {
37            if (show_serve_error.passed()) {
38                // Note: this error is unexpected and might indicate a severe problem (deadlock, broken design, ...)
39                fprintf(stderr, "Error serving DB (while waiting for sync '%s'): %s\n", sync_id, serve_error);
40                show_serve_error.restart(1.7);
41            }
42        }
43
44        GB_transaction ta(gb_main);
45        GB_CSTR value = GB_read_sync_value(gb_main, sync_id);
46        if (value) {
47            if (strcmp(value, SYNC_STATE_FINISHED) == 0) {
48                fprintf(stderr, "reached expected sync on ID '%s': current value is '%s'\n", sync_id, value);
49                sync_reached = true;
50            }
51            else if (strcmp(value, SYNC_STATE_STARTED) != 0) {
52                error = GBS_global_string("sync-ID '%s' has unexpected value '%s'", sync_id, value);
53            }
54            else {
55                if (show_wait.passed()) {
56                    fprintf(stderr, "waiting for sync on ID '%s': current value is '%s'\n", sync_id, value);
57                    show_wait.restart(1.7);
58                }
59            }
60        }
61        else {
62            error = GB_await_error();
63        }
64    }
65
66    return error;
67}
68
69GB_ERROR ARB_system(const char *cmd, XCmdType boundExectype) {
70    XCMD_TYPE  exectype = boundExectype.get_type();
71    GBDATA    *gb_main  = boundExectype.get_gb_main();
72
73    // runs a command in an xterm
74
75    bool  background         = exectype & _XCMD__ASYNC;     // true -> run asynchronous
76    bool  requires_DB_access = exectype & _XCMD__ACCESS_DB; // true -> assume shell process requires DB access
77    bool  always_wait_key    = exectype & _XCMD__WAITKEY;   // true -> always wait for keypress (otherwise only wait if 'cmd' fails)
78    bool  hidden             = exectype & _XCMD__HIDDEN;    // true -> do not use terminal window
79    char *sync_via_ID        = NULp;
80
81    if (!background && requires_DB_access) {
82        if (GB_is_server(gb_main)) {
83            static int sync_counter = 0;
84            sync_via_ID             = GBS_global_string_copy("xcmd%i", ++sync_counter);
85
86            // run shell command asynchronously (synchronisation will be done via database)
87            background = true;
88
89            fprintf(stderr, "synchronise command using ID '%s'. command=%s\n", sync_via_ID, cmd);
90        }
91        // else: we are a client -> database access is possible from synchronous shell command
92        // (Note: this might fail, if the client itself currently blocks the server)
93    }
94
95    const int     BUFSIZE = 4096;
96    GBS_strstruct system_call(BUFSIZE);
97
98    system_call.put('(');
99    if (!hidden) system_call.cat(GB_getenvARB_XCMD());
100
101    {
102        GBS_strstruct bash_command(BUFSIZE);
103
104        GB_CSTR ldlibpath = GB_getenv("LD_LIBRARY_PATH"); // due to SIP ldlibpath is always NULL under osx
105                                                          // (should not matter because all binaries are linked statically)
106
107        if (ldlibpath) {
108            bash_command.cat("LD_LIBRARY_PATH=");
109            {
110                char *dquoted_library_path = GBK_doublequote(ldlibpath);
111                bash_command.cat(dquoted_library_path);
112                free(dquoted_library_path);
113            }
114            bash_command.cat(";export LD_LIBRARY_PATH;");
115        }
116#if !defined(DARWIN)
117        else {
118            // under Linux it normally is a problem if LD_LIBRARY_PATH is undefined
119            // -> warn in terminal to provide a hint:
120            bash_command.cat("echo 'Warning: LD_LIBRARY_PATH is undefined';");
121        }
122#endif
123
124        bash_command.cat(" (");
125        bash_command.cat(cmd);
126
127        {
128            char *sync_command        = sync_via_ID ? GBS_global_string_copy("arb_sync --write %s " SYNC_STATE_FINISHED, sync_via_ID) : NULp;
129            const char *wait_commands = hidden ? "sleep 1" : "echo; echo 'Press ENTER to close this window'; read a";
130
131            if (always_wait_key) {
132                bash_command.cat("; [ $? -ne 0 ] && echo \"Command terminated with failure (exit code $?)\""); // display failure
133                bash_command.cat("; ");
134            }
135            else {
136                bash_command.cat("; xc=$?");  // store exitcode of command
137                if (sync_command) {
138                    bash_command.cat("; ");
139                    bash_command.cat(sync_command); // sync arb macro execution
140                }
141                bash_command.cat("; [ $xc -ne 0 ] && echo \"Command terminated with failure (exit code $xc)\""); // display failure
142                bash_command.cat("; exit $xc"); // restore exitcode of command
143                bash_command.cat(") || (");
144            }
145            bash_command.cat(wait_commands);
146            if (always_wait_key && sync_command) {
147                bash_command.cat("; ");
148                bash_command.cat(sync_command); // sync arb macro execution
149            }
150            bash_command.put(')');
151
152            free(sync_command);
153        }
154
155        char *squoted_bash_command = GBK_singlequote(bash_command.get_data());
156        system_call.cat(" bash -c ");
157        system_call.cat(squoted_bash_command);
158        free(squoted_bash_command);
159    }
160    system_call.cat(" )");
161    if (background) system_call.cat(" &");
162
163    GB_ERROR error = NULp;
164    if (sync_via_ID) {
165        GB_transaction ta(gb_main);
166        error = GB_write_sync_value(gb_main, sync_via_ID, SYNC_STATE_STARTED);
167    }
168
169    if (!error) {
170        error = GBK_system(system_call.get_data());
171        // if error occurs here (or before) -> sync value will never change
172    }
173
174    if (sync_via_ID && !error) {
175        error = wait_for_sync__servingDB(gb_main, sync_via_ID);
176    }
177
178    freenull(sync_via_ID);
179
180    return error;
181}
182
183void ARB_system_in_console_cb(AW_window*, const char *command, const XCmdType *exectype) {
184    // like ARB_system, but usable as WindowCallback + displays errors as message.
185    aw_message_if(ARB_system(command, *exectype));
186}
187
Note: See TracBrowser for help on using the repository browser.