| 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 |  | 
|---|
| 19 | using namespace std; | 
|---|
| 20 |  | 
|---|
| 21 | // --------------------------- | 
|---|
| 22 | //      external commands | 
|---|
| 23 |  | 
|---|
| 24 | #define SYNC_STATE_STARTED  "started" | 
|---|
| 25 | #define SYNC_STATE_FINISHED "finished" | 
|---|
| 26 |  | 
|---|
| 27 | static 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 |  | 
|---|
| 69 | GB_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 |  | 
|---|
| 183 | void 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 |  | 
|---|