1: /*
2: * $Id: init.c,v 1.18 2026/03/19 19:03:58 snw Exp $
3: * FreeM initialization
4: *
5: *
6: * Author: Serena Willis <snw@coherent-logic.com>
7: * Copyright (C) 1998 MUG Deutschland
8: * Copyright (C) 2020, 2025 Coherent Logic Development LLC
9: *
10: *
11: * This file is part of FreeM.
12: *
13: * FreeM is free software: you can redistribute it and/or modify
14: * it under the terms of the GNU Affero Public License as published by
15: * the Free Software Foundation, either version 3 of the License, or
16: * (at your option) any later version.
17: *
18: * FreeM is distributed in the hope that it will be useful,
19: * but WITHOUT ANY WARRANTY; without even the implied warranty of
20: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21: * GNU Affero Public License for more details.
22: *
23: * You should have received a copy of the GNU Affero Public License
24: * along with FreeM. If not, see <https://www.gnu.org/licenses/>.
25: *
26: * $Log: init.c,v $
27: * Revision 1.18 2026/03/19 19:03:58 snw
28: * Attempt to rectify terminal corruption problem when the user performs a gracious exit
29: *
30: * Revision 1.17 2026/03/14 21:04:51 snw
31: * Fix segfault in OPEN command with I/O channel specified but no file
32: *
33: * Revision 1.16 2026/01/07 19:51:33 snw
34: * Fix segfault in reverse $QUERY
35: *
36: * Revision 1.15 2025/05/16 04:02:14 snw
37: * Make FreeM build on macOS on Apple Silicon
38: *
39: * Revision 1.14 2025/04/20 03:13:17 snw
40: * Updates to init_execution_context
41: *
42: * Revision 1.13 2025/04/17 14:34:27 snw
43: * Further logging improvements
44: *
45: * Revision 1.12 2025/04/15 14:39:06 snw
46: * Further improvements to logging
47: *
48: * Revision 1.11 2025/04/13 04:22:43 snw
49: * Fix snprintf calls
50: *
51: * Revision 1.10 2025/04/10 01:24:38 snw
52: * Remove C++ style comments
53: *
54: * Revision 1.9 2025/04/03 16:58:34 snw
55: * Make error message for shm_init error during initialization more friendly
56: *
57: * Revision 1.8 2025/03/24 04:44:55 snw
58: * Don't call ttyname on OS/2
59: *
60: * Revision 1.7 2025/03/24 04:05:36 snw
61: * Replace crlf with frm_crlf to avoid symbol conflict with readline on OS/2
62: *
63: * Revision 1.6 2025/03/09 19:14:25 snw
64: * First phase of REUSE compliance and header reformat
65: *
66: *
67: * SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
68: * SPDX-License-Identifier: AGPL-3.0-or-later
69: **/
70:
71: #include <stdio.h>
72: #include <stdlib.h>
73: #include <string.h>
74: #include <unistd.h>
75: #include <limits.h>
76: #include <sys/types.h>
77: #include <sys/stat.h>
78: #include <pwd.h>
79: #include <time.h>
80: #include <errno.h>
81: #include <sys/ioctl.h>
82:
83: #if !defined(__APPLE__) && !defined(__gnu_hurd__) && !defined(EMSCRIPTEN)
84: # if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__AMIGA)
85: # include <termios.h>
86: # if !defined(__AMIGA)
87: # define TCGETA TIOCGETA
88: # define TCSETA TIOCSETA
89: # endif
90: # define termio termios
91: # else
92: # if !defined(MSDOS) && !defined(__linux__)
93: # include <termio.h>
94: # else
95: # include <termios.h>
96: # define termio termios
97: # endif
98: # endif
99: #else
100: # include <termios.h>
101: #endif
102:
103:
104: #if defined(__APPLE__)
105: # define termio termios
106: # define TCGETA TIOCGETA
107: # define TCSETA TIOCSETA
108: #endif
109:
110: #include "config.h"
111:
112: #if defined(HAVE_MWAPI_MOTIF)
113: # include <Xm/Xm.h>
114: #endif
115:
116: #include "mpsdef.h"
117: #include "transact.h"
118: #include "namespace.h"
119: #include "events.h"
120: #include "mdebug.h"
121: #include "shmmgr.h"
122: #include "locktab.h"
123: #include "jobtab.h"
124: #include "datatypes.h"
125: #include "objects.h"
126: #include "log.h"
127:
128: #ifdef HAVE_LIBREADLINE
129: # if defined(HAVE_READLINE_READLINE_H)
130: # include <readline/readline.h>
131: # elif defined(HAVE_READLINE_H)
132: # include <readline.h>
133: # else /* !defined(HAVE_READLINE_H) */
134: extern char *readline ();
135: # endif /* !defined(HAVE_READLINE_H) */
136: char *cmdline = NULL;
137: #else /* !defined(HAVE_READLINE_READLINE_H) */
138: /* no readline */
139: #endif /* HAVE_LIBREADLINE */
140:
141: #ifdef HAVE_READLINE_HISTORY
142: # if defined(HAVE_READLINE_HISTORY_H)
143: # include <readline/history.h>
144: # elif defined(HAVE_HISTORY_H)
145: # include <history.h>
146: # else /* !defined(HAVE_HISTORY_H) */
147: extern void add_history ();
148: extern int write_history ();
149: extern int read_history ();
150: # endif /* defined(HAVE_READLINE_HISTORY_H) */
151: /* no history */
152: #endif /* HAVE_READLINE_HISTORY */
153:
154: #if defined(HAVE_WIRINGPI_H)
155: # include <wiringPi.h>
156: #endif
157:
158: #if !defined(PATH_MAX) && defined(_SCO_DS)
159: # define PATH_MAX 4096
160: #endif
161:
162: #if !defined(PATH_MAX) && defined(__gnu_hurd__)
163: # define PATH_MAX 1024
164: #endif
165:
166: #if !defined(PATH_MAX) && defined(__sun__)
167: # include <limits.h>
168: #endif
169:
170: #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
171: # include <sys/syslimits.h>
172: #endif
173:
174: #define SHMKEY 0x990120
175: #define SHMSIZ 1048576
176:
177: void init_process(void);
178: void init_devtable(void);
179: void init_signals(void);
180: void init_timezone(void);
181: void init_freem_path(void);
182:
183: #if defined(HAVE_LIBREADLINE)
184: void init_readline(void);
185: #endif
186:
187: void init_execution_context(void);
188: void init_io(void);
189: void init_random_number(void);
190: void init_ztrap(void);
191: void init_ssvn(void);
192: void init_terminal(void);
193: void init_estack(void);
194:
195: void init_mwapi(void);
196:
197: short init (char *namespace_name)
198: {
199: short retval;
200: int log_level;
201:
202: init_log ();
203: init_process ();
204: init_devtable ();
205: init_signals ();
206: init_freem_path ();
207: init_timezone ();
208:
209: #if defined(HAVE_LIBREADLINE)
210: init_readline ();
211: #endif
212:
213: init_execution_context ();
214:
215: if (run_daemon == FALSE) {
216: init_io ();
217: }
218:
219: init_random_number ();
220: init_ztrap ();
221:
222: retval = shm_init (shm_init_size);
223:
224: if (retval == SHMS_GET_ERR) {
225: if (errno != 13) {
226: logprintf (FM_LOG_FATAL, "init: error initializing shared memory (error code %d [%s])", errno, strerror (errno));
227: }
228: else {
229: logprintf (FM_LOG_FATAL, "init: error attaching to environment -- does your user belong to the group that owns environment '%s'?", shm_env);
230: }
231: }
232:
233: symtab_init ();
234: tp_init ();
235:
236: set_namespace (namespace_name, FALSE);
237:
238: if (first_process) {
239: log_level = FM_LOG_INFO;
240: }
241: else {
242: log_level = FM_LOG_DEBUG;
243: }
244:
245: if (first_process) {
246: logprintf (log_level, "init: we are the first process in the environment (pid %d)", pid);
247: }
248:
249: logprintf (log_level, "init: initializing job table");
250: jobtab_init ();
251:
252: logprintf (log_level, "init: adding job to job table");
253: job_init (FALSE);
254:
255: logprintf (log_level, "init: initializing structured system variables");
256: init_ssvn ();
257:
258: logprintf (log_level, "init: initializing terminal");
259: init_terminal ();
260:
261: logprintf (log_level, "init: initializing asynchronous events");
262: evt_init ();
263:
264: logprintf (log_level, "init: initializing debugger");
265: dbg_init ();
266:
267: logprintf (log_level, "init: initializing error stack");
268: init_estack();
269:
270: etrap[0] = EOL;
271: ecode[0] = EOL;
272: estack = 0;
273:
274: init_mwapi();
275:
276: if (merr () == OK) {
277: return TRUE;
278: }
279:
280: return FALSE;
281: }
282:
283: void init_process (void)
284: {
285: pid = getpid (); /* get $J = process ID */
286: umask (0); /* protection bits mask to full rights */
287: snprintf (fp_conversion, sizeof (fp_conversion) - 1, "%%.%df\201", DBL_DIG);
288:
289: if (fp_mode) {
290: zprecise = DBL_DIG;
291: }
292: else {
293: zprecise = 100;
294: }
295: }
296:
297: void init_devtable (void)
298: {
299: register int i;
300: register int j;
301:
302: for (j = 0; j <= MAXDEV; j++) { /* init. translation tables */
303:
304: for (i = 0; i < 256; i++) {
305: G0I[j][i] = (char) i;
306: G0O[j][i] = (char) i;
307: G1I[j][i] = (char) i;
308: G1O[j][i] = (char) i;
309: }
310:
311: G0I[j][UNSIGN (EOL)] = NUL;
312: G0O[j][UNSIGN (EOL)] = NUL;
313: G1I[j][UNSIGN (EOL)] = NUL;
314: G1O[j][UNSIGN (EOL)] = NUL;
315: G0I[j][UNSIGN (DELIM)] = NUL;
316: G0O[j][UNSIGN (DELIM)] = NUL;
317: G1I[j][UNSIGN (DELIM)] = NUL;
318: G1O[j][UNSIGN (DELIM)] = NUL;
319: G0I[j][256] = EOL;
320: G0O[j][256] = EOL;
321: G1I[j][256] = EOL;
322: G1O[j][256] = EOL;
323:
324: }
325:
326: #ifdef SCO
327: #ifndef HACK_NOXLATE
328: G0I[HOME][245] = 64;
329: G0O[HOME][64] = 245; /* Paragraph */
330: G0I[HOME][142] = 91;
331: G0O[HOME][91] = 142; /* A umlaut */
332: G0I[HOME][153] = 92;
333: G0O[HOME][92] = 153; /* O umlaut */
334: G0I[HOME][154] = 93;
335: G0O[HOME][93] = 154; /* U umlaut */
336: G0I[HOME][132] = 123;
337: G0O[HOME][123] = 132; /* a umlaut */
338: G0I[HOME][148] = 124;
339: G0O[HOME][124] = 148; /* o umlaut */
340: G0I[HOME][129] = 125;
341: G0O[HOME][125] = 129; /* u umlaut */
342: G0I[HOME][225] = 126;
343: G0O[HOME][126] = 225; /* sharp s */
344: #endif/*HACK_NOXLATE*/
345:
346: /* DEC Special graphics */
347: G1I[HOME][254] = 96;
348: G1O[HOME][96] = 254; /* diamond */
349: G1I[HOME][176] = 97;
350: G1O[HOME][97] = 176; /* checker board */
351: G1I[HOME][241] = 99;
352: G1O[HOME][99] = 241; /* FF */
353: G1I[HOME][242] = 100;
354: G1O[HOME][100] = 242; /* CR */
355: G1I[HOME][243] = 101;
356: G1O[HOME][101] = 243; /* LF */
357: G1I[HOME][248] = 102;
358: G1O[HOME][102] = 248; /* degree sign */
359: G1I[HOME][241] = 103;
360: G1O[HOME][103] = 241; /* plus minus */
361: G1I[HOME][244] = 104;
362: G1O[HOME][104] = 244; /* NL */
363: G1I[HOME][251] = 105;
364: G1O[HOME][105] = 251; /* VT */
365: G1I[HOME][217] = 106;
366: G1O[HOME][106] = 217; /* lower right corner */
367: G1I[HOME][191] = 107;
368: G1O[HOME][107] = 191; /* upper right corner */
369: G1I[HOME][218] = 108;
370: G1O[HOME][108] = 218; /* upper left corner */
371: G1I[HOME][192] = 109;
372: G1O[HOME][109] = 192; /* lower left corner */
373: G1I[HOME][197] = 110;
374: G1O[HOME][110] = 197; /* cross */
375: G1I[HOME][200] = 111;
376: G1O[HOME][111] = 200; /* linescan 5 */
377: G1I[HOME][201] = 112;
378: G1O[HOME][112] = 201; /* linescan 4 */
379: G1I[HOME][196] = 113;
380: G1O[HOME][113] = 196; /* linescan 3 */
381: G1I[HOME][202] = 114;
382: G1O[HOME][114] = 202; /* linescan 2 */
383: G1I[HOME][203] = 115;
384: G1O[HOME][115] = 203; /* linescan 1 */
385: G1I[HOME][195] = 116;
386: G1O[HOME][116] = 195; /* left junction */
387: G1I[HOME][180] = 117;
388: G1O[HOME][117] = 180; /* right junction */
389: G1I[HOME][193] = 118;
390: G1O[HOME][118] = 193; /* lower junction */
391: G1I[HOME][194] = 119;
392: G1O[HOME][119] = 194; /* upper junction */
393: G1I[HOME][179] = 120;
394: G1O[HOME][120] = 179; /* vertival bar */
395: G1I[HOME][243] = 121;
396: G1O[HOME][121] = 243; /* lower equals */
397: G1I[HOME][242] = 122;
398: G1O[HOME][122] = 242; /* greater equals */
399: G1I[HOME][227] = 123;
400: G1O[HOME][123] = 227; /* pi */
401: G1I[HOME][246] = 124;
402: G1O[HOME][124] = 246; /* not equals */
403: G1I[HOME][128] = 125;
404: G1O[HOME][125] = 128; /* euro sign */
405: G1I[HOME][250] = 126;
406: G1O[HOME][126] = 250; /* centered dot */
407: #endif /* SCO */
408: }
409:
410: void init_signals (void)
411: {
412: sig_init ();
413: }
414:
415: void init_timezone (void)
416: {
417:
418: struct tm lt;
419: struct tm gt;
420:
421: unsigned long gmt;
422: unsigned long lmt;
423:
424: long clock;
425:
426: #if defined(__CYGWIN__)
427: tzset (); /* may be required in order */
428: /* to guarantee _timezone set */
429: #else
430:
431: clock = time (0L);
432: lt = *localtime (&clock);
433: gt = *gmtime (&clock);
434:
435: /* This is awkward but I think it is portable: steve_morris */
436: gmt = gt.tm_year * 365;
437: gmt = (gmt + gt.tm_yday) * 24;
438: gmt = (gmt + gt.tm_hour) * 60;
439: gmt = (gmt + gt.tm_min);
440:
441: lmt = lt.tm_year * 365;
442: lmt = (lmt + lt.tm_yday) * 24;
443: lmt = (lmt + lt.tm_hour) * 60;
444: lmt = (lmt + lt.tm_min);
445:
446: FreeM_timezone = (gmt - lmt) * 60;
447: tzoffset = -FreeM_timezone;
448:
449: #endif /* __CYGWIN__ */
450:
451:
452: }
453:
454: void init_freem_path (void)
455: {
456:
457: if((freem_path = malloc(PATH_MAX + 1)) == NULL) {
458: fprintf(stderr, "Can't allocate freem_path. Exiting.");
459:
460: exit(1);
461: }
462:
463: freem_path[0] = NUL;
464:
465: /* check where I'm being executed from */
466: #ifdef __linux__
467: readlink ("/proc/self/exe", freem_path, PATH_MAX);
468: #endif
469: #ifdef __FreeBSD__
470: readlink ("/proc/curproc/file", freem_path, PATH_MAX);
471: #endif
472: #ifdef __sun
473: readlink ("/proc/self/path/a.out", freem_path, PATH_MAX);
474: #endif
475:
476: if(freem_path[0] == NUL) {
477: /* we don't know where we came from */
478: }
479:
480: getcwd (curdir, PATHLEN);
481: stcnv_c2m (curdir);
482:
483: }
484:
485: #if defined(HAVE_LIBREADLINE)
486: void init_readline (void)
487: {
488: uid_t uid = geteuid ();
489: struct passwd *pw = getpwuid (uid);
490: char *pw_buf;
491:
492: pw_buf = (char *) calloc (strlen(pw->pw_dir) + 1, sizeof(char));
493: strcpy (pw_buf, pw->pw_dir);
494:
495: snprintf (history_file, sizeof (history_file) - 1, "%s/.freem_history", pw_buf);
496:
497: free (pw_buf);
498:
499: using_history ();
500: read_history (history_file);
501: }
502: #endif
503:
504: void init_execution_context (void)
505: {
506: register int i;
507:
508: obj_init ();
509:
510: merr_clear ();
511:
512: codptr = code;
513: code[0] = EOL; /* init code_pointer */
514:
515: if ((partition = calloc ((unsigned) (PSIZE + 2), 1)) == NULL) {
516: logprintf (FM_LOG_FATAL, "init_execution_context: could not allocate symbol table (error code %d [%s])", errno, strerror (errno));
517: }
518:
519: for (i = 0; i < MAXNO_OF_RBUF; i++) {
520: rbuf_flags[i].standard = standard;
521: }
522:
523: for (i = 0; i < NESTLEVLS; i++) {
524: extr_types[i] = DT_STRING;
525: }
526:
527: symlen = PSIZE;
528: s = &partition[PSIZE] - 256; /* pointer to symlen_offset */
529: argptr = partition; /* pointer to beg of tmp-storage */
530:
531: if ((svntable = calloc ((unsigned) (UDFSVSIZ + 1), 1)) == NULL) {
532: logprintf (FM_LOG_FATAL, "init_execution_context: could not allocate user-defined ISV table (error code %d [%s])", errno, strerror (errno));
533: }
534: svnlen = UDFSVSIZ; /* begin of udf_svn_table */
535:
536: if ((buff = calloc ((unsigned) NO_OF_RBUF * (unsigned) PSIZE0, 1)) == NULL) {
537: logprintf (FM_LOG_FATAL, "init_execution_context: could not allocate routine buffers (error code %d [%s])", errno, strerror (errno));
538: }
539:
540: if ((newstack = calloc ((unsigned) NSIZE, 1)) == NULL) {
541: logprintf (FM_LOG_FATAL, "init_execution_context: could not allocate NEW stack (error code %d [%s])", errno, strerror (errno));
542: }
543:
544: logprintf (FM_LOG_DEBUG, "allocating newptr stack...");
545:
546: newptr = newstack;
547: newlimit = newstack + NSIZE - 1024;
548:
549:
550: if ((namstck = calloc ((unsigned) NESTLEVLS * 13, 1)) == NULL) {
551: logprintf (FM_LOG_FATAL, "init_execution_context: could not allocate newptr stack (error code %d [%s])", errno, strerror (errno));
552: }
553:
554: *namstck = EOL;
555: *(namstck + 1) = EOL;
556: namptr = namstck; /* routine name stack pointer */
557:
558: if ((framstck = calloc ((unsigned) NESTLEVLS * 256, 1)) == NULL) {
559: logprintf (FM_LOG_FATAL, "init_execution_context: could not allocate DO frame stack stack (error code %d [%s])", errno, strerror (errno));
560: }
561:
562: *framstck = EOL;
563: *(framstck + 1) = EOL;
564: dofrmptr = framstck; /* DO_frame stack pointer */
565:
566: if ((cmdstack = calloc ((unsigned) NESTLEVLS * 256, 1)) == NULL) {
567: logprintf (FM_LOG_FATAL, "init_execution_context: could not allocate command stack (error code %d [%s])", errno, strerror (errno));
568: }
569:
570: cmdptr = cmdstack; /* command stack */
571:
572: rouend = rouins = rouptr = buff;
573: roucur = buff + (NO_OF_RBUF * PSIZE0 + 1);
574: *rouptr = EOL;
575: *(rouptr + 1) = EOL;
576: *(rouptr + 2) = EOL;
577:
578: err_suppl[0] = EOL; /* empty out supplemental error info */
579: }
580:
581: void init_estack (void)
582: {
583: stcpy (merr_stack[0].PLACE, "xecline()\201");
584: }
585:
586: #if defined(HAVE_MWAPI_MOTIF)
587: void init_mwapi (void)
588: {
589: /*
590: if (getenv("DISPLAY") != NULL) {
591: gtk_init (0, NULL);
592: }
593: */
594: /* TODO: init Motif/libXt */
595: }
596: #else
597: void init_mwapi (void)
598: {
599: return;
600: }
601: #endif
602:
603: void init_io (void)
604: {
605: register int i;
606:
607: /* initialize screen */
608: setbuf (stdin, NULL); /* no input buffering */
609: glvnflag.all = 0L;
610: stcpy (buff, "\201");
611: writeHOME (buff);
612: sq_modes[0] = '+';
613: for (i = 0; i <= MAXDEV; ug_buf[i++][0] = EOL); /* init read-buffers */
614:
615: frm_crlf[HOME] = frm_filter;
616:
617: if (hardcopy) zbreakon = ENABLE; /* enable CTRL/B */
618:
619: set_io (MUMPS); /* set i/o parameters */
620:
621: #if !defined(__AMIGA) && !defined(__OS2__)
622: if (ttyname (HOME)) { /* for $IO of HOME */
623: strcpy (dev[HOME], ttyname (HOME));
624: dev[HOME][strlen (dev[HOME])] = EOL;
625: }
626: else {
627: dev[HOME][0] = EOL; /* ...we are in a pipe */
628: }
629: for (i = 5; i <= MAXDEV; i++) dev[i][0] = EOL;
630: #else
631: #if defined(__AMIGA)
632: strcpy (dev[HOME], "CONSOLE:");
633: #else
634: #if defined(__OS2__)
635: strcpy (dev[HOME], "CON:");
636: #endif
637: #endif
638: #endif
639:
640: /* init function keys */
641: for (i = 0; i < 44; zfunkey[i++][0] = EOL);
642: }
643:
644: void init_random_number (void)
645: {
646:
647: srand (time (NULL));
648:
649: if ((nrandom = time (0L) * getpid ()) < 0) {
650: nrandom = (-nrandom);
651: }
652:
653: }
654:
655: void init_ztrap (void)
656: {
657:
658: if (frm_filter) {
659: ztrap[0][0] = EOL; /* no default ztrap for filters */
660: }
661: else if (startuprou[0] == '^') {
662: stcpy (ztrap[0], startuprou);
663: }
664: else {
665: stcpy (ztrap[0], "^%SYSINIT\201");
666: }
667:
668: /* $ZT to be xecuted on startup */
669:
670: stcpy (ztrap[NESTLEVLS + 1], ztrap[0]); /* DSM V.2 error trapping */
671:
672: }
673:
674: void init_ssvn(void)
675: {
676: ssvn_job_update ();
677: ssvn_display_update ();
678: ssvn_routine_update ();
679: ssvn_library_update ();
680: if (first_process) ssvn_system_update ();
681: }
682:
683: void init_terminal(void)
684: {
685: xpos[HOME] = 80;
686: ypos[HOME] = 24;
687: }
688:
689: void reset_terminal(void)
690: {
691: struct termio tpara;
692:
693: ioctl (0, TCGETA, &tpara);
694:
695: tpara.c_lflag |= (ECHO | ICANON); /* enable echo/no cbreak mode */
696: tpara.c_iflag |= ICRNL; /* cr-lf mapping */
697: tpara.c_oflag |= ONLCR; /* cr-lf mapping */
698: tpara.c_cc[VMIN] = EOT;
699: tpara.c_cc[VTIME] = -1;
700:
701: ioctl (0, TCSETA, &tpara);
702: }
703:
704: void cleanup (void)
705: {
706: char k_buf[256];
707: int ch;
708:
709: /* remove this job's entry from ^$JOB SSVN */
710: snprintf (k_buf, sizeof (k_buf) - 1, "^$JOB\202%d\201", pid);
711: symtab_shm (kill_sym, k_buf, " \201");
712:
713: reset_terminal ();
714:
715: if (tp_level > 0) {
716:
717: if (direct_mode == TRUE) {
718: fprintf (stderr, "UNCOMMITTED TRANSACTIONS EXIST:\n\n");
719: tp_tdump ();
720: set_io (UNIX);
721: fprintf (stderr, "\nWould you like to c)ommit or r)ollback the above transactions and their operations? ($TLEVEL = %d) ", tp_level);
722:
723: for (;;) {
724: ch = fgetc (stdin);
725:
726: if (ch == 'c' || ch == 'C') {
727: while (tp_level > 0) tp_tcommit ();
728:
729: fprintf (stderr, "\n\nTransactions have been committed.\n");
730:
731: break;
732: }
733: else if (ch == 'r' || ch == 'R') {
734: tp_trollback (tp_level);
735:
736: fprintf (stderr, "\n\nTransactions have been rolled back.\n");
737:
738: break;
739: }
740: else {
741: fprintf (stderr, "\n\nInvalid input '%c'. Must choose c)ommit or r)ollback.\n", ch);
742: }
743: }
744: }
745: else {
746: fprintf (stderr, "Uncommitted transactions exist. Rolling back.\n");
747: tp_trollback (tp_level);
748: }
749: }
750:
751: #if defined(HAVE_LIBREADLINE)
752: write_history (history_file);
753: #endif
754:
755: locktab_unlock_all ();
756: job_remove (pid);
757:
758: shm_exit ();
759:
760: if (run_daemon == TRUE) {
761:
762: if (pid_fd != -1) {
763: lockf (pid_fd, F_ULOCK, 0);
764: close (pid_fd);
765: }
766:
767: if (pid_file_path != NULL) {
768: unlink (pid_file_path);
769: }
770:
771: }
772:
773: free (buff); /* free previously allocated space */
774: free (svntable);
775: if (partition) free (partition);
776: if (apartition) free (apartition);
777:
778:
779: free (newstack);
780:
781:
782: if (v22size) free (v22ali);
783:
784: return;
785: } /* end of cleanup */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>