1 /*
2  *  gretl -- Gnu Regression, Econometrics and Time-series Library
3  *  Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include "gretl.h"
21 #include "gretl_ipc.h"
22 
23 #if defined(__linux) || defined(linux)
24 # include <dirent.h>
25 # include <signal.h>
26 #elif defined(WIN32)
27 # include <windows.h>
28 # include <tlhelp32.h>
29 #elif defined(HAVE_LIBPROC_H) && defined(HAVE_SYS_PROC_INFO_H)
30 # define BSD_PROC 1
31 # include <sys/proc_info.h>
32 # include <libproc.h>
33 #endif
34 
35 #if defined(WIN32) || defined(BSD_PROC)
36 # define N_PIDS 8
37 #endif
38 
39 #define IPC_DEBUG 0
40 
41 /* gretl_ipc: inter-process communication
42 
43    First, the functions conditional on the GRETL_PID_FILE definition
44    are to do with the file gretl.pid in the user's "dotdir"; this is
45    used to put a "sequence number" into the gretl main window title
46    bar in case the user chooses to run more than one concurrent gretl
47    process.
48 
49    Second, the functions conditional on GRETL_OPEN_HANDLER are to do
50    with the case where a user has one or more gretl processes running
51    already, and performs an action that would by default cause a new
52    instance to be started. We give the user the option to activate an
53    existing gretl instance rather than starting a new one.
54 
55    The GRETL_PID_FILE functionality is supported for Linux, MS Windows
56    and systems with BSD libproc. The GRETL_OPEN_HANDLER functionality
57    is supported for Linux and Windows only. Note that on Mac the OS
58    preserves uniqueness of GUI application instances by default -- so
59    the situation is the opposite of that on Linux and Windows. In the
60    GTK-quartz version of gretl we offer a top-level menu item to
61    override this and launch a second (or third, etc.) gretl instance.
62 
63    GRETL_OPEN_HANDLER depends on GRETL_PID_FILE for its mechanism but
64    not vice versa.
65 */
66 
67 #ifdef GRETL_PID_FILE
68 
69 static int my_sequence_number;
70 
gretl_sequence_number(void)71 int gretl_sequence_number (void)
72 {
73     return my_sequence_number;
74 }
75 
76 # if defined(WIN32)
77 
78 /* Fill the @gpids array with PIDs of running gretl processes
79    (other than self, given by @mypid). We use this array to
80    validate PIDs read from dotdir/gretl.pid. Note that we
81    only consider up to N_PIDS = 8 running gretl processes,
82    which hopefully should be more than enough.
83 */
84 
get_prior_gretl_pids(long * gpids,long mypid)85 static void get_prior_gretl_pids (long *gpids, long mypid)
86 {
87     HANDLE hsnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
88 
89     if (hsnap) {
90         PROCESSENTRY32 pe;
91 	int match, j = 0;
92 	char *s;
93 
94         pe.dwSize = sizeof(PROCESSENTRY32);
95         if (Process32First(hsnap, &pe)) {
96             do {
97 		if (pe.th32ProcessID != mypid) {
98 		    s = strrchr(pe.szExeFile, '\\');
99 		    if (s != NULL) {
100 			match = !strcmp(s + 1, "gretl.exe");
101 		    } else {
102 			match = !strcmp(pe.szExeFile, "gretl.exe");
103 		    }
104 		    if (match && j < N_PIDS) {
105 			gpids[j++] = pe.th32ProcessID;
106 		    }
107 		}
108             } while (Process32Next(hsnap, &pe));
109 	}
110 	CloseHandle(hsnap);
111     }
112 }
113 
114 # elif defined(BSD_PROC)
115 
116 /* The Mac/BSD variant of the above */
117 
get_prior_gretl_pids(long * gpids,long mypid)118 static void get_prior_gretl_pids (long *gpids, long mypid)
119 {
120     int nproc = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
121     char buf[PROC_PIDPATHINFO_MAXSIZE];
122     size_t psize;
123     pid_t *pids;
124     int i, j, match;
125 
126     psize = nproc * sizeof *pids;
127     pids = calloc(nproc, sizeof *pids);
128     if (pids == NULL) {
129 	return;
130     }
131 
132     proc_listpids(PROC_ALL_PIDS, 0, pids, psize);
133 
134     j = 0;
135     for (i=0; i<nproc; i++) {
136 	if (pids[i] == 0 || pids[i] == mypid) {
137 	    continue;
138 	}
139 	memset(buf, 0, sizeof buf);
140 	proc_pidpath(pids[i], buf, sizeof buf);
141 	if (*buf != '\0') {
142 	    char *s = strrchr(buf, '/');
143 
144 	    if (s != NULL) {
145 	        match = !strcmp("gretl", s + 1);
146             } else {
147 	        match = !strcmp("gretl", buf);
148             }
149 	    if (match && j < N_PIDS) {
150 		gpids[j++] = (long) pids[i];
151 	    }
152 	}
153     }
154 
155     free(pids);
156 }
157 
158 # endif /* get_prior_gretl_pids() variants */
159 
160 # if defined(__linux) || defined(linux)
161 
162 /* Given the PID @test, retrieved from dotdir/gretl.pid,
163    see if it really represents a prior gretl process. We do
164    this to guard against the possibility that gretl crashes on
165    some occasion (or something else weird happens), in which
166    case the multi-instance "sequence number" would get screwed up.
167 
168    Return 1 if @test is OK, zero otherwise.
169 */
170 
pid_is_valid(long test,long mypid)171 static int pid_is_valid (long test, long mypid)
172 {
173     char buf[128];
174     char pname[32] = {0};
175     FILE *fp;
176     int err = 0;
177 
178     snprintf(buf, sizeof buf, "/proc/%ld/stat", test);
179     fp = gretl_fopen(buf, "r");
180 
181     if (fp == NULL) {
182 	/* no such pid? */
183 	return 0;
184     } else {
185 	char state;
186 	long pid;
187 
188 	if ((fscanf(fp, "%ld (%31[^)]) %c", &pid, pname, &state)) != 3) {
189 	    /* huh? */
190 	    err = 1;
191 	} else if (strcmp(pname, "gretl_x11") && strcmp(pname, "lt-gretl_x11")) {
192 	    /* it's not gretl */
193 	    err = 1;
194 	} else if (pid == mypid) {
195 	    /* it's not really a prior instance */
196 	    err = 1;
197 	}
198 	fclose(fp);
199     }
200 
201     return !err;
202 }
203 
204 # else
205 
206 /* Windows and Mac: in these cases we have to construct
207    an array of "good" PIDs against which to test.
208 */
209 
pid_is_valid(long test,long mypid)210 static int pid_is_valid (long test, long mypid)
211 {
212     static long gpids[N_PIDS] = {0};
213     static int gotpids;
214     int i;
215 
216     if (!gotpids) {
217 	/* construct array of valid gretl PIDs */
218 	get_prior_gretl_pids(gpids, mypid);
219 	gotpids = 1;
220     }
221 
222     /* check @test against the known good PIDs */
223     for (i=0; i<N_PIDS && gpids[i]; i++) {
224 	if (test == gpids[i]) {
225 	    return 1;
226 	}
227     }
228 
229     return 0;
230 }
231 
232 # endif /* PID validation variants */
233 
234 /* Having found one or more invalid PIDs in dotdir/gretl.pid,
235    scratch them out.
236 */
237 
prune_pid_file(const char * pidfile,FILE * f1,char * buf,int bufsize,long mypid)238 static int prune_pid_file (const char *pidfile, FILE *f1,
239 			   char *buf, int bufsize,
240 			   long mypid)
241 {
242     char newfile[FILENAME_MAX];
243     FILE *f2;
244     int err = 0;
245 
246     sprintf(newfile, "%sgretlpid.tmp", gretl_dotdir());
247     f2 = gretl_fopen(newfile, "wb");
248 
249     if (f2 == NULL) {
250 	err = E_FOPEN;
251     } else {
252 	long pid;
253 	int m;
254 
255 	while (fgets(buf, bufsize, f1)) {
256 	    sscanf(buf, "%ld %d", &pid, &m);
257 	    if (pid_is_valid(pid, mypid)) {
258 		fputs(buf, f2);
259 	    }
260 	}
261 
262 	fclose(f2);
263 	fclose(f1);
264 
265 	err = gretl_copy_file(newfile, pidfile);
266 	if (err) {
267 	    fprintf(stderr, "copy pid file: err = %d\n", err);
268 	}
269 	gretl_remove(newfile);
270     }
271 
272     return err;
273 }
274 
275 /* On start-up, write our PID and sequence number into gretl.pid in
276    dotdir; while we're at it, we check any other (putative) gretl PIDs
277    recorded in that file just in case they've gone bad (e.g. gretl
278    crashed or the OS crashed).
279 */
280 
write_pid_to_file(void)281 int write_pid_to_file (void)
282 {
283     const char *dotdir;
284     char pidfile[FILENAME_MAX];
285     FILE *f1;
286     long mypid;
287     int err = 0;
288 
289 #ifdef WIN32
290     mypid = (long) GetCurrentProcessId();
291 #else
292     mypid = (long) getpid();
293 #endif
294 
295     dotdir = gretl_dotdir();
296     sprintf(pidfile, "%sgretl.pid", dotdir);
297     f1 = gretl_fopen(pidfile, "rb");
298 
299     if (f1 == NULL) {
300 	/* no pid file, starting from scratch */
301 	f1 = gretl_fopen(pidfile, "wb");
302 	if (f1 == NULL) {
303 	    err = E_FOPEN;
304 	} else {
305 	    fprintf(f1, "%ld 1\n", mypid);
306 	    fclose(f1);
307 	}
308     } else {
309 	/* we already have a pid file (open as f1) */
310 	char newfile[FILENAME_MAX];
311 	char buf[32];
312 	FILE *f2;
313 	long pid;
314 	int m, n = 0;
315 
316 	sprintf(newfile, "%sgretlpid.tmp", dotdir);
317 	f2 = gretl_fopen(newfile, "wb");
318 
319 	if (f2 == NULL) {
320 	    err = E_FOPEN;
321 	} else {
322 	    while (fgets(buf, sizeof buf, f1)) {
323 		sscanf(buf, "%ld %d", &pid, &m);
324 		if (pid_is_valid(pid, mypid)) {
325 		    fputs(buf, f2);
326 		    n = m;
327 		}
328 	    }
329 	    n++;
330 	    fprintf(f2, "%ld %d\n", mypid, n);
331 	    fclose(f2);
332 	    my_sequence_number = n;
333 	}
334 
335 	fclose(f1);
336 
337 	if (err) {
338 	    gretl_remove(pidfile);
339 	} else {
340 	    err = gretl_copy_file(newfile, pidfile);
341 	    if (err) {
342 		fprintf(stderr, "copy pid file: err = %d\n", err);
343 	    }
344 	    gretl_remove(newfile);
345 	}
346     }
347 
348     return err;
349 }
350 
351 /* On exit, delete our PID from gretl.pid in dotdir */
352 
delete_pid_from_file(void)353 void delete_pid_from_file (void)
354 {
355     const char *dotdir;
356     char pidfile[FILENAME_MAX];
357     FILE *f1;
358     long mypid;
359     int err = 0;
360 
361 #ifdef WIN32
362     mypid = (long) GetCurrentProcessId();
363 #else
364     mypid = (long) getpid();
365 #endif
366 
367     dotdir = gretl_dotdir();
368     sprintf(pidfile, "%sgretl.pid", dotdir);
369 
370     f1 = gretl_fopen(pidfile, "rb");
371     if (f1 == NULL) {
372 	err = E_FOPEN;
373     } else {
374 	char newfile[FILENAME_MAX];
375 	char buf[32];
376 	FILE *f2;
377 	long pid;
378 	int nleft = 0;
379 
380 	sprintf(newfile, "%sgretlpid.tmp", dotdir);
381 	f2 = gretl_fopen(newfile, "wb");
382 
383 	if (f2 == NULL) {
384 	    err = E_FOPEN;
385 	} else {
386 	    while (fgets(buf, sizeof buf, f1)) {
387 		sscanf(buf, "%ld", &pid);
388 		if (pid != mypid) {
389 		    fputs(buf, f2);
390 		    nleft++;
391 		}
392 	    }
393 	    fclose(f2);
394 	}
395 
396 	fclose(f1);
397 
398 	if (err) {
399 	    gretl_remove(pidfile);
400 	} else if (nleft > 0) {
401 	    err = gretl_copy_file(newfile, pidfile);
402 	    gretl_remove(newfile);
403 	} else {
404 	    gretl_remove(newfile);
405 	    gretl_remove(pidfile);
406 	}
407     }
408 }
409 
410 #endif /* GRETL_PID_FILE */
411 
412 /* Now come the more ambitious open-handler functions,
413    implemented for Linux and Windows but not required
414    for Mac.
415 */
416 
417 #ifdef GRETL_OPEN_HANDLER
418 
419 # ifdef WIN32
420 /* message number for inter-program communication */
421 static UINT WM_GRETL;
422 # endif
423 
424 /* See if we can find the PID of (the last) prior gretl
425    instance. If so, return the PID, otherwise return 0.
426    We read from dotdir/gretl.pid in the first instance
427    but then validate any PID(s) we find against the current
428    process table.
429 */
430 
gretl_prior_instance(void)431 long gretl_prior_instance (void)
432 {
433     char pidfile[FILENAME_MAX];
434     FILE *fp;
435     long mypid, ret = 0;
436 
437 # ifdef WIN32
438     if (WM_GRETL == 0) {
439 	WM_GRETL = RegisterWindowMessage((LPCTSTR) "gretl_message");
440     }
441     mypid = (long) GetCurrentProcessId();
442 # else
443     mypid = getpid();
444 # endif
445 
446     sprintf(pidfile, "%sgretl.pid", gretl_dotdir());
447     fp = gretl_fopen(pidfile, "rb");
448 
449 #if IPC_DEBUG
450     fprintf(stderr, "*** gretl_prior_instance: pidfile='%s', fp=%p\n",
451 	    pidfile, (void *) fp);
452 #endif
453 
454     if (fp != NULL) {
455 	char buf[32];
456 	int prune = 0;
457 	int ok, n_valid = 0;
458 	long tmp;
459 
460 	while (fgets(buf, sizeof buf, fp)) {
461 	    sscanf(buf, "%ld", &tmp);
462 	    ok = pid_is_valid(tmp, mypid);
463 #if IPC_DEBUG
464 	    fprintf(stderr, " got prior PID %d, ok = %d\n", (int) tmp, ok);
465 #endif
466 	    if (ok) {
467 		ret = tmp;
468 		n_valid++;
469 	    } else {
470 		prune = 1;
471 	    }
472 	}
473 
474 	if (n_valid == 0) {
475 	    /* trash the pid file */
476 	    fclose(fp);
477 	    gretl_remove(pidfile);
478 	} else if (prune) {
479 	    /* rewrite the pid file */
480 	    rewind(fp);
481 	    prune_pid_file(pidfile, fp, buf, sizeof buf, mypid);
482 	} else {
483 	    /* leave well alone */
484 	    fclose(fp);
485 	}
486     }
487 
488     return ret;
489 }
490 
491 /* Process a message coming from another gretl instance.  Such a
492    message calls for a hand-off to "this" instance, and some
493    information regarding the hand-off is contained in a little file in
494    the user's dotdir, with a name on the pattern "open-<pid>" where
495    <pid> should equal "this" instance's PID.
496 
497    If the hand-off involves opening a file (which will be the case if
498    the trigger is double-clicking on a gretl-associated file), the
499    name of this file is written into the IPC file; otherwise the IPC
500    file contains the word "none".
501 */
502 
process_handoff_message(void)503 static void process_handoff_message (void)
504 {
505     char fname[FILENAME_MAX];
506     FILE *fp;
507     int try_open = 0;
508     long mypid;
509 
510 #ifdef WIN32
511     mypid = (long) GetCurrentProcessId();
512 #else
513     mypid = (long) getpid();
514 #endif
515 
516     sprintf(fname, "%sopen-%ld", gretl_dotdir(), mypid);
517     fp = gretl_fopen(fname, "rb");
518 
519 #if IPC_DEBUG
520     fprintf(stderr, "*** process_handoff_message\n"
521 	    " fname='%s', fp = %p\n", fname, (void *) fp);
522 #endif
523 
524     if (fp != NULL) {
525 	char path[FILENAME_MAX];
526 
527 	if (fgets(path, sizeof path, fp)) {
528 	    tailstrip(path);
529 	    if (strcmp(path, "none")) {
530 		set_tryfile(path);
531 		try_open = 1;
532 	    }
533 	}
534 	fclose(fp);
535     }
536 
537     remove(fname);
538 
539     gtk_window_present(GTK_WINDOW(mdata->main));
540 
541     if (try_open) {
542 	/* we picked up the name of a file to open */
543 	open_tryfile();
544     }
545 }
546 
write_request_file(long gpid,const char * fname)547 static gboolean write_request_file (long gpid, const char *fname)
548 {
549     char tmpname[FILENAME_MAX];
550     FILE *fp;
551 
552     sprintf(tmpname, "%sopen-%ld", gretl_dotdir(), gpid);
553     fp = gretl_fopen(tmpname, "wb");
554 
555     if (fp == NULL) {
556 	return FALSE;
557     } else {
558 	fprintf(fp, "%s\n", *fname ? fname : "none");
559 	fclose(fp);
560     }
561 
562     return TRUE;
563 }
564 
565 # if defined(__linux) || defined(linux)
566 
567 /* Signal handler for the case where a newly started gretl process
568    hands off to a previously running process, passing the name of a
569    file to be opened via a small file in the user's dotdir.
570 */
571 
open_handler(int sig,siginfo_t * sinfo,void * context)572 static void open_handler (int sig, siginfo_t *sinfo, void *context)
573 {
574     if (sig == SIGUSR1 && sinfo->si_code == SI_QUEUE) {
575 	if (sinfo->si_uid == getuid() &&
576 	    sinfo->si_value.sival_ptr == (void *) 0xf0) {
577 	}
578 	process_handoff_message();
579     }
580 }
581 
582 /* linux: at start-up, install a handler for SIGUSR1 */
583 
install_open_handler(void)584 int install_open_handler (void)
585 {
586     static struct sigaction action;
587 
588     action.sa_sigaction = open_handler;
589     sigemptyset(&action.sa_mask);
590     action.sa_flags = SA_SIGINFO;
591 
592     return sigaction(SIGUSR1, &action, NULL);
593 }
594 
595 /* For the case where a new gretl process has been started (possibly
596    in response to double-clicking a suitable filename), but the user
597    doesn't really want a new process and would prefer that the file be
598    opened in a previously running gretl instance. We send SIGUSR1 to
599    the prior instance and exit. The @fname argument is used if the new
600    instance of gretl was invoked with a filename argument.
601 */
602 
forward_open_request(long gpid,const char * fname)603 gboolean forward_open_request (long gpid, const char *fname)
604 {
605     gboolean ok = write_request_file(gpid, fname);
606 
607 #if IPC_DEBUG
608     fprintf(stderr, "*** forward_open_request\n");
609 #endif
610 
611     if (ok) {
612 	union sigval data;
613 
614 	data.sival_ptr = (void *) 0xf0;
615 	/* note: gpid is the PID of the previously running
616 	   process to which the signal should be sent
617 	*/
618 	ok = (sigqueue(gpid, SIGUSR1, data) == 0);
619     }
620 
621     return ok;
622 }
623 
624 # elif defined(WIN32)
625 
626 /* Below: since POSIX signals are not supported on Windows,
627    we implement the equivalent of the above functionality
628    for Linux using the Windows messaging API.
629 */
630 
631 /* If we receive a special WM_GRETL message from another gretl
632    instance, process it and remove it from the message queue;
633    otherwise hand the message off to GDK.
634 */
635 
mdata_filter(GdkXEvent * xevent,GdkEvent * event,gpointer data)636 static GdkFilterReturn mdata_filter (GdkXEvent *xevent,
637 				     GdkEvent *event,
638 				     gpointer data)
639 {
640     MSG *msg = (MSG *) xevent;
641 
642     if (msg->message == WM_GRETL && msg->wParam == 0xf0) {
643 	fprintf(stderr, "mdata_filter: got WM_GRETL + 0xf0\n");
644 	process_handoff_message();
645 	return GDK_FILTER_REMOVE;
646     }
647 
648     return GDK_FILTER_CONTINUE;
649 }
650 
651 /* Add a filter to intercept WM_GRETL messages, which would
652    otherwise get discarded by GDK */
653 
install_open_handler(void)654 int install_open_handler (void)
655 {
656     gdk_window_add_filter(NULL, mdata_filter, NULL);
657     return 0;
658 }
659 
660 /* Some window handles matched by pid seem to be
661    spurious: here we screen them and accept only
662    a plausible target for messaging.
663 */
664 
plausible_target(HWND hw)665 static int plausible_target (HWND hw)
666 {
667     WINDOWINFO wi = {0};
668     char s[64];
669     int ret = 0;
670 
671     wi.cbSize = sizeof(WINDOWINFO);
672 
673     if (GetWindowInfo(hw, &wi) != 0) {
674 	if ((wi.dwStyle & WS_CAPTION) &&
675 	    (wi.dwExStyle & WS_EX_ACCEPTFILES)) {
676 	    s[0] = '\0';
677 	    GetWindowTextA(hw, s, 63);
678 	    if (!strcmp(s, "gretl") || !strncmp(s, "gretl: session ", 15)) {
679 		return 1;
680 	    }
681 	}
682     }
683 
684     return 0;
685 }
686 
687 /* Find the window handle associated with a given PID:
688    this is used when we've found the PID of a running
689    gretl instance and we need its window handle for
690    use with SendMessage().
691 */
692 
get_hwnd_for_pid(long gpid)693 static HWND get_hwnd_for_pid (long gpid)
694 {
695     HWND hw = NULL, ret = NULL;
696     DWORD pid;
697 
698     do {
699         hw = FindWindowEx(NULL, hw, NULL, NULL);
700 	if (hw == NULL) {
701 	    break;
702 	}
703         GetWindowThreadProcessId(hw, &pid);
704 	if ((long) pid == gpid) {
705 	    /* PID matched */
706 	    if (plausible_target(hw)) {
707 		/* looks like main gretl window */
708 		ret = hw;
709 	    }
710         }
711     } while (ret == NULL);
712 
713     return ret;
714 }
715 
716 /* Try forwarding a request to the prior gretl instance with
717    PID @gpid, either to open a file or just to show itself.
718 
719    Return TRUE if we're able to do this, otherwise FALSE.
720 */
721 
forward_open_request(long gpid,const char * fname)722 gboolean forward_open_request (long gpid, const char *fname)
723 {
724     HWND hw = get_hwnd_for_pid(gpid);
725     gboolean ok = FALSE;
726 
727     if (hw != NULL) {
728 	long mypid = (long) GetCurrentProcessId();
729 
730 	ok = write_request_file(gpid, fname);
731 #if IPC_DEBUG
732 	fprintf(stderr, "forward_open_request: mypid=%d, ok=%d\n", (int) mypid, ok);
733 #endif
734 	if (ok) {
735 	    SendMessage(hw, WM_GRETL, 0xf0, mypid);
736 	}
737     }
738 
739     return ok;
740 }
741 
742 # endif /* OS variations */
743 #endif /* GRETL_OPEN_HANDLER */
744