source: trunk/mondo/src/common/libmondo-fork.c @ 1081

Last change on this file since 1081 was 1081, checked in by bruno, 12 years ago

merge -r1078:1080 $SVN_M/branches/stable

  • Property svn:keywords set to Id
File size: 18.2 KB
Line 
1/* libmondo-fork.c - subroutines for handling forking/pthreads/etc.
2 * $Id: libmondo-fork.c 1081 2007-01-28 22:20:07Z bruno $
3 */
4#include "my-stuff.h"
5#include "mr_mem.h"
6#include "mondostructures.h"
7#include "libmondo-fork.h"
8#include "libmondo-string-EXT.h"
9#include "newt-specific-EXT.h"
10#include "libmondo-files-EXT.h"
11#include "libmondo-tools-EXT.h"
12#include "mr_mem.h"
13
14extern bool g_text_mode;
15pid_t g_buffer_pid = 0;
16
17
18/**
19 * Call a program and retrieve its last line of output.
20 * @param call The program to run.
21 * @return The last line of its output.
22 * @note The returned value should be freed by caller
23 */
24char *call_program_and_get_last_line_of_output(char *call)
25{
26    char *result = NULL;
27    FILE *fin = NULL;
28    size_t n = 0;
29
30    assert_string_is_neither_NULL_nor_zerolength(call);
31
32    if ((fin = popen(call, "r"))) {
33        for (mr_getline(&result, &n, fin); !feof(fin); mr_getline(&result, &n, fin));
34        paranoid_pclose(fin);
35    } else {
36        log_OS_error("Unable to popen call");
37    }
38    return(result);
39}
40
41#define MONDO_POPMSG  _("Your PC will not retract the CD tray automatically. Please call mondoarchive with the -m (manual CD tray) flag.")
42
43/**
44 * Call mkisofs to create an ISO image.
45 * @param bkpinfo The backup information structure. Fields used:
46 * - @c bkpinfo->manual_cd_tray
47 * - @c bkpinfo->backup_media_type
48 * - @c bkpinfo->please_dont_eject_when_restoring
49 * @param basic_call The call to mkisofs. May contain tokens that will be resolved to actual data. The tokens are:
50 * - @c _ISO_ will become the ISO file (@p isofile)
51 * - @c _CD#_ becomes the CD number (@p cd_no)
52 * - @c _ERR_ becomes the logfile (@p g_logfile)
53 * @param isofile Replaces @c _ISO_ in @p basic_call. Should probably be the ISO image to create (-o parameter to mkisofs).
54 * @param cd_no Replaces @c _CD#_ in @p basic_call. Should probably be the CD number.
55 * @param logstub Unused.
56 * @param what_i_am_doing The action taking place (e.g. "Making ISO #1"). Used as the title of the progress dialog.
57 * @return Exit code of @c mkisofs (0 is success, anything else indicates failure).
58 * @bug @p logstub is unused.
59 */
60int
61eval_call_to_make_ISO(struct s_bkpinfo *bkpinfo,
62                      char *basic_call, char *isofile,
63                      int cd_no, char *logstub, char *what_i_am_doing)
64{
65
66    /*@ int's  *** */
67    int retval = 0;
68
69
70    /*@ buffers      *** */
71    char *midway_call, *ultimate_call, *tmp, *command,
72        *cd_number_str;
73    char *p;
74
75/*@***********   End Variables ***************************************/
76
77    log_msg(3, "Starting");
78    assert(bkpinfo != NULL);
79    assert_string_is_neither_NULL_nor_zerolength(basic_call);
80    assert_string_is_neither_NULL_nor_zerolength(isofile);
81    assert_string_is_neither_NULL_nor_zerolength(logstub);
82    midway_call = mr_malloc(1200);
83    ultimate_call = mr_malloc(1200);
84    tmp = mr_malloc(1200);
85
86    mr_asprintf(&cd_number_str, "%d", cd_no);
87    resolve_naff_tokens(midway_call, basic_call, isofile, "_ISO_");
88    resolve_naff_tokens(tmp, midway_call, cd_number_str, "_CD#_");
89    mr_free(cd_number_str);
90
91    resolve_naff_tokens(ultimate_call, tmp, MONDO_LOGFILE, "_ERR_");
92    log_msg(4, "basic call = '%s'", basic_call);
93    log_msg(4, "midway_call = '%s'", midway_call);
94    log_msg(4, "tmp = '%s'", tmp);
95    log_msg(4, "ultimate call = '%s'", ultimate_call);
96    mr_asprintf(&command, "%s >> %s", ultimate_call, MONDO_LOGFILE);
97
98    log_to_screen
99        (_("Please be patient. Do not be alarmed by on-screen inactivity."));
100    log_msg(4, "Calling open_evalcall_form() with what_i_am_doing='%s'",
101            what_i_am_doing);
102    if (bkpinfo->manual_cd_tray) {
103        /* Find everything after a 2>> and remove it */
104        p = strstr(command, "2>>");
105        if (p) {
106            for (; *p != ' ' && *p != '\0'; p++) {
107                *p = ' ';
108            }
109        }
110#ifndef _XWIN
111        if (!g_text_mode) {
112            newtSuspend();
113        }
114#endif
115        log_msg(1, "command = '%s'", command);
116        retval += system(command);
117        if (!g_text_mode) {
118            newtResume();
119        }
120        if (retval) {
121            log_msg(2, "Basic call '%s' returned an error.", basic_call);
122            popup_and_OK(_("Press ENTER to continue."));
123            popup_and_OK
124                (_("mkisofs and/or cdrecord returned an error. CD was not created"));
125        }
126    }
127    /* if text mode then do the above & RETURN; if not text mode, do this... */
128    else {
129        log_msg(3, "command = '%s'", command);
130//      yes_this_is_a_goto:
131        retval =
132            run_external_binary_with_percentage_indicator_NEW
133            (what_i_am_doing, command);
134    }
135    mr_free(command);
136
137    mr_free(midway_call);
138    mr_free(ultimate_call);
139    mr_free(tmp);
140    return (retval);
141}
142
143
144/**
145 * Run a program and log its output (stdout and stderr) to the logfile.
146 * @param program The program to run. Passed to the shell, so you can use pipes etc.
147 * @param debug_level If @p g_loglevel is higher than this, do not log the output.
148 * @return The exit code of @p program (depends on the command, but 0 almost always indicates success).
149 */
150int run_program_and_log_output(char *program, int debug_level)
151{
152    /*@ buffer ****************************************************** */
153    char *callstr = NULL;
154    char *incoming = NULL;
155
156    /*@ int ********************************************************* */
157    int res = 0;
158    size_t n = 0;
159    bool log_if_failure = FALSE;
160    bool log_if_success = FALSE;
161
162    /*@ pointers *************************************************** */
163    FILE *fin;
164    char *p;
165
166    /*@ end vars *************************************************** */
167
168    assert(program != NULL);
169    if (!program[0]) {
170        log_msg(2, "Warning - asked to run zerolength program");
171        return (1);
172    }
173
174    if (debug_level <= g_loglevel) {
175        log_if_success = TRUE;
176        log_if_failure = TRUE;
177    }
178    mr_asprintf(&callstr,
179            "%s > /tmp/mondo-run-prog-thing.tmp 2> /tmp/mondo-run-prog-thing.err",
180            program);
181    while ((p = strchr(callstr, '\r'))) {
182        *p = ' ';
183    }
184    while ((p = strchr(callstr, '\n'))) {
185        *p = ' ';
186    }                           /* single '=' is intentional */
187
188
189    res = system(callstr);
190    if (((res == 0) && log_if_success) || ((res != 0) && log_if_failure)) {
191        log_msg(0, "running: %s", callstr);
192        log_msg(0,
193                "--------------------------------start of output-----------------------------");
194    }
195    mr_free(callstr);
196
197    if (log_if_failure
198        &&
199        system
200        ("cat /tmp/mondo-run-prog-thing.err >> /tmp/mondo-run-prog-thing.tmp 2> /dev/null"))
201    {
202        log_OS_error("Command failed");
203    }
204    unlink("/tmp/mondo-run-prog-thing.err");
205    fin = fopen("/tmp/mondo-run-prog-thing.tmp", "r");
206    if (fin) {
207        for (mr_getline(&incoming, &n, fin); !feof(fin);
208             mr_getline(&incoming, &n, fin)) {
209            /* patch by Heiko Schlittermann */
210            p = incoming;
211            while (p && *p) {
212                if ((p = strchr(p, '%'))) {
213                    memmove(p, p + 1, strlen(p) + 1);
214                    p += 2;
215                }
216            }
217            /* end of patch */
218            strip_spaces(incoming);
219            if ((res == 0 && log_if_success)
220                || (res != 0 && log_if_failure)) {
221                log_msg(0, incoming);
222            }
223        }
224        mr_free(incoming);
225        paranoid_fclose(fin);
226    }
227    unlink("/tmp/mondo-run-prog-thing.tmp");
228    if ((res == 0 && log_if_success) || (res != 0 && log_if_failure)) {
229        log_msg(0,
230                "--------------------------------end of output------------------------------");
231        if (res) {
232            log_msg(0, "...ran with res=%d", res);
233        } else {
234            log_msg(0, "...ran just fine. :-)");
235        }
236    }
237//  else
238//    { log_msg (0, "-------------------------------ran w/ res=%d------------------------------", res); }
239    return (res);
240}
241
242
243/**
244 * Run a program and log its output to the screen.
245 * @param basic_call The program to run.
246 * @param what_i_am_doing The title of the evalcall form.
247 * @return The return value of the command (varies, but 0 almost always means success).
248 * @see run_program_and_log_output
249 * @see log_to_screen
250 */
251int run_program_and_log_to_screen(char *basic_call, char *what_i_am_doing)
252{
253    /*@ int ******************************************************** */
254    int retval = 0;
255    int res = 0;
256    int i;
257
258    /*@ pointers **************************************************** */
259    FILE *fin;
260
261    /*@ buffers **************************************************** */
262    char *tmp;
263    char *command;
264    char *lockfile;
265
266    /*@ end vars *************************************************** */
267
268    assert_string_is_neither_NULL_nor_zerolength(basic_call);
269
270    mr_asprintf(&lockfile, "/tmp/mojo-jojo.blah.XXXXXX");
271    mkstemp(lockfile);
272    mr_asprintf(&command,
273            "echo hi > %s ; %s >> %s 2>> %s; res=$?; sleep 1; rm -f %s; exit $res",
274            lockfile, basic_call, MONDO_LOGFILE, MONDO_LOGFILE, lockfile);
275    open_evalcall_form(what_i_am_doing);
276    mr_asprintf(&tmp, "Executing %s", basic_call);
277    log_msg(2, tmp);
278    mr_free(tmp);
279
280    if (!(fin = popen(command, "r"))) {
281        log_OS_error("Unable to popen-in command");
282        mr_asprintf(&tmp, _("Failed utterly to call '%s'"), command);
283        log_to_screen(tmp);
284        mr_free(tmp);
285        mr_free(lockfile);
286        mr_free(command);
287        return (1);
288    }
289    mr_free(command);
290
291    if (!does_file_exist(lockfile)) {
292        log_to_screen(_("Waiting for external binary to start"));
293        for (i = 0; i < 60 && !does_file_exist(lockfile); sleep(1), i++) {
294            log_msg(3, "Waiting for lockfile %s to exist", lockfile);
295        }
296    }
297    /* This works on Newt, and it gives quicker updates. */
298    for (; does_file_exist(lockfile); sleep(1)) {
299        log_file_end_to_screen(MONDO_LOGFILE, "");
300        update_evalcall_form(1);
301    }
302    /* Evaluate the status returned by pclose to get the exit code of the called program. */
303    errno = 0;
304    res = pclose(fin);
305    /* Log actual pclose errors. */
306    if (errno)
307        log_msg(5, "pclose err: %d", errno);
308    /* Check if we have a valid status. If we do, extract the called program's exit code. */
309    /* If we don't, highlight this fact by returning -1. */
310    if (WIFEXITED(res)) {
311        retval = WEXITSTATUS(res);
312    } else {
313        retval = -1;
314    }
315    close_evalcall_form();
316    unlink(lockfile);
317    mr_free(lockfile);
318
319    return (retval);
320}
321
322
323/**
324 * Apparently used. @bug This has a purpose, but what?
325 */
326#define PIMP_START_SZ "STARTSTARTSTART9ff3kff9a82gv34r7fghbkaBBC2T231hc81h42vws8"
327#define PIMP_END_SZ "ENDENDEND0xBBC10xBBC2T231hc81h42vws89ff3kff9a82gv34r7fghbka"
328
329
330
331
332int copy_from_src_to_dest(FILE * f_orig, FILE * f_archived, char direction)
333{
334// if dir=='w' then copy from orig to archived
335// if dir=='r' then copy from archived to orig
336    char *tmp = NULL;
337    char *buf = NULL;
338    long int bytes_to_be_read, bytes_read_in, bytes_written_out =
339        0, bufcap, subsliceno = 0;
340    int retval = 0;
341    FILE *fin = NULL;
342    FILE *fout = NULL;
343    FILE *ftmp = NULL;
344
345    log_msg(5, "Opening.");
346    bufcap = 256L * 1024L;
347    buf = mr_malloc(bufcap);
348
349    if (direction == 'w') {
350        fin = f_orig;
351        fout = f_archived;
352        mr_asprintf(&tmp, "%-64s", PIMP_START_SZ);
353        if (fwrite(tmp, 1, 64, fout) != 64) {
354            fatal_error("Can't write the introductory block");
355        }
356        mr_free(tmp);
357
358        while (1) {
359            bytes_to_be_read = bytes_read_in = fread(buf, 1, bufcap, fin);
360            if (bytes_read_in == 0) {
361                break;
362            }
363            mr_asprintf(&tmp, "%-64ld", bytes_read_in);
364            if (fwrite(tmp, 1, 64, fout) != 64) {
365                fatal_error("Cannot write introductory block");
366            }
367            mr_free(tmp);
368
369            log_msg(7,
370                    "subslice #%ld --- I have read %ld of %ld bytes in from f_orig",
371                    subsliceno, bytes_read_in, bytes_to_be_read);
372            bytes_written_out += fwrite(buf, 1, bytes_read_in, fout);
373            mr_asprintf(&tmp, "%-64ld", subsliceno);
374            if (fwrite(tmp, 1, 64, fout) != 64) {
375                fatal_error("Cannot write post-thingy block");
376            }
377            mr_free(tmp);
378
379            log_msg(7, "Subslice #%d written OK", subsliceno);
380            subsliceno++;
381        }
382        mr_asprintf(&tmp, "%-64ld", 0L);
383        if (fwrite(tmp, 1, 64L, fout) != 64L) {
384            fatal_error("Cannot write final introductory block");
385        }
386    } else {
387        fin = f_archived;
388        fout = f_orig;
389        if (!(tmp = malloc(64L))) {
390            fatal_error("Failed to malloc() tmp");
391        }
392        if (fread(tmp, 1, 64L, fin) != 64L) {
393            fatal_error("Cannot read the introductory block");
394        }
395        log_msg(5, "tmp is %s", tmp);
396        if (!strstr(tmp, PIMP_START_SZ)) {
397            fatal_error("Can't find intro blk");
398        }
399        if (fread(tmp, 1, 64L, fin) != 64L) {
400            fatal_error("Cannot read introductory blk");
401        }
402        bytes_to_be_read = atol(tmp);
403        while (bytes_to_be_read > 0) {
404            log_msg(7, "subslice#%ld, bytes=%ld", subsliceno,
405                    bytes_to_be_read);
406            bytes_read_in = fread(buf, 1, bytes_to_be_read, fin);
407            if (bytes_read_in != bytes_to_be_read) {
408                fatal_error
409                    ("Danger, WIll Robinson. Failed to read whole subvol from archives.");
410            }
411            bytes_written_out += fwrite(buf, 1, bytes_read_in, fout);
412            if (fread(tmp, 1, 64L, fin) != 64L) {
413                fatal_error("Cannot read post-thingy block");
414            }
415            if (atol(tmp) != subsliceno) {
416                log_msg(1, "Wanted subslice %ld but got %ld ('%s')",
417                        subsliceno, atol(tmp), tmp);
418            }
419            log_msg(7, "Subslice #%ld read OK", subsliceno);
420            subsliceno++;
421            if (fread(tmp, 1, 64L, fin) != 64L) {
422                fatal_error("Cannot read introductory block");
423            }
424            bytes_to_be_read = atol(tmp);
425        }
426    }
427
428//  log_msg(4, "Written %ld of %ld bytes", bytes_written_out, bytes_read_in);
429
430    if (direction == 'w') {
431        mr_free(tmp);
432        mr_asprintf(&tmp, "%-64s", PIMP_END_SZ);
433        if (fwrite(tmp, 1, 64L, fout) != 64L) {
434            fatal_error("Can't write the final block");
435        }
436        mr_free(tmp);
437    } else {
438        log_msg(1, "tmpA is %s", tmp);
439        if (!strstr(tmp, PIMP_END_SZ)) {
440            if (fread(tmp, 1, 64L, fin) != 64L) {
441                fatal_error("Can't read the final block");
442            }
443            log_msg(5, "tmpB is %s", tmp);
444            if (!strstr(tmp, PIMP_END_SZ)) {
445                ftmp = fopen("/tmp/out.leftover", "w");
446                bytes_read_in = fread(tmp, 1, 64L, fin);
447                log_msg(1, "bytes_read_in = %ld", bytes_read_in);
448//      if (bytes_read_in!=128+64) { fatal_error("Can't read the terminating block"); }
449                fwrite(tmp, 1, bytes_read_in, ftmp);
450               
451                mr_free(tmp);
452                if (!(tmp = malloc(512))) {
453                    fatal_error("Failed to malloc() tmp");
454                }
455                /* BERLIOS : strange ???
456                s-printf(tmp, "I am here - %llu", ftello(fin));
457                log_msg(0, tmp);
458                */
459                fread(tmp, 1, 512, fin);
460                log_msg(0, "tmp = '%s'", tmp);
461                fwrite(tmp, 1, 512, ftmp);
462                fclose(ftmp);
463                fatal_error("Missing terminating block");
464            }
465        }
466        mr_free(tmp);
467    }
468
469    mr_free(buf);
470    log_msg(3, "Successfully copied %ld bytes", bytes_written_out);
471    return (retval);
472}
473
474/**
475 * Feed @p input_device through ntfsclone to @p output_fname.
476 * @param input_device The device to image.
477 * @param output_fname The file to write.
478 * @return 0 for success, nonzero for failure.
479 */
480int feed_into_ntfsprog(char *input_device, char *output_fname)
481{
482// BACKUP
483    int res = -1;
484    char *command = NULL;
485    char *tmp = NULL;
486
487    if (!does_file_exist(input_device)) {
488        fatal_error("input device does not exist");
489    }
490    tmp = find_home_of_exe("ntfsclone");
491    if (!tmp) {
492        fatal_error("ntfsclone not found");
493    }
494    mr_free(tmp);
495
496    mr_asprintf(&command, "ntfsclone --force --save-image --overwrite %s %s", output_fname, input_device);
497    res = run_program_and_log_output(command, 5);
498    mr_free(command);
499
500    unlink(output_fname);
501    return (res);
502}
503
504
505int run_external_binary_with_percentage_indicator_OLD(char *tt, char *cmd)
506{
507
508    int res = 0;
509    int percentage = 0;
510    int maxpc = 0;
511    int pcno = 0;
512    int last_pcno = 0;
513
514    char *command = NULL;
515    char *tempfile = NULL;
516    FILE *pin = NULL;
517
518    assert_string_is_neither_NULL_nor_zerolength(cmd);
519
520    tempfile = call_program_and_get_last_line_of_output("mktemp -q /tmp/mondo.XXXXXXXX");
521    mr_asprintf(&command, "%s >> %s 2>> %s; rm -f %s", cmd, tempfile, tempfile,
522            tempfile);
523    log_msg(3, command);
524    open_evalcall_form(tt);
525
526    if (!(pin = popen(command, "r"))) {
527        log_OS_error("fmt err");
528        mr_free(command);
529        mr_free(tempfile);
530        return (1);
531    }
532    mr_free(command);
533
534    maxpc = 100;
535// OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
536    for (sleep(1); does_file_exist(tempfile); sleep(1)) {
537        pcno = grab_percentage_from_last_line_of_file(MONDO_LOGFILE);
538        if (pcno < 0 || pcno > 100) {
539            log_msg(5, "Weird pc#");
540            continue;
541        }
542        percentage = pcno * 100 / maxpc;
543        if (pcno <= 5 && last_pcno > 40) {
544            close_evalcall_form();
545            open_evalcall_form("_(Verifying...");
546        }
547        last_pcno = pcno;
548        update_evalcall_form(percentage);
549    }
550// OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
551    close_evalcall_form();
552    if (pclose(pin)) {
553        res++;
554        log_OS_error("Unable to pclose");
555    }
556    unlink(tempfile);
557    mr_free(tempfile);
558    return (res);
559}
560
561
562void *run_prog_in_bkgd_then_exit(void *info)
563{
564    char *sz_command;
565    static int res = 4444;
566
567    res = 999;
568    sz_command = (char *) info;
569    log_msg(4, "sz_command = '%s'", sz_command);
570    res = system(sz_command);
571    if (res > 256 && res != 4444) {
572        res = res / 256;
573    }
574    log_msg(4, "child res = %d", res);
575    sz_command[0] = '\0';
576    pthread_exit((void *) (&res));
577}
578
579
580int run_external_binary_with_percentage_indicator_NEW(char *tt, char *cmd)
581{
582
583    /*@ int *************************************************************** */
584    int res = 0;
585    int percentage = 0;
586    int maxpc = 100;
587    int pcno = 0;
588    int last_pcno = 0;
589    int counter = 0;
590
591    /*@ buffers *********************************************************** */
592    char *command;
593    /*@ pointers ********************************************************** */
594    static int chldres = 0;
595    int *pchild_result;
596    pthread_t childthread;
597
598    pchild_result = &chldres;
599    assert_string_is_neither_NULL_nor_zerolength(cmd);
600    assert_string_is_neither_NULL_nor_zerolength(tt);
601    *pchild_result = 999;
602
603    mr_asprintf(&command, "%s 2>> %s", cmd, MONDO_LOGFILE);
604    log_msg(3, "command = '%s'", command);
605    if ((res =
606         pthread_create(&childthread, NULL, run_prog_in_bkgd_then_exit,
607                        (void *) command))) {
608        fatal_error("Unable to create an archival thread");
609    }
610
611    log_msg(8, "Parent running");
612    open_evalcall_form(tt);
613    for (sleep(1); command[0] != '\0'; sleep(1)) {
614        pcno = grab_percentage_from_last_line_of_file(MONDO_LOGFILE);
615        if (pcno <= 0 || pcno > 100) {
616            log_msg(8, "Weird pc#");
617            continue;
618        }
619        percentage = pcno * 100 / maxpc;
620        if (pcno <= 5 && last_pcno >= 40) {
621            close_evalcall_form();
622            open_evalcall_form(_("Verifying..."));
623        }
624        if (counter++ >= 5) {
625            counter = 0;
626            log_file_end_to_screen(MONDO_LOGFILE, "");
627        }
628        last_pcno = pcno;
629        update_evalcall_form(percentage);
630    }
631    mr_free(command);
632
633    log_file_end_to_screen(MONDO_LOGFILE, "");
634    close_evalcall_form();
635    pthread_join(childthread, (void *) (&pchild_result));
636    if (pchild_result) {
637        res = *pchild_result;
638    } else {
639        res = 666;
640    }
641    log_msg(3, "Parent res = %d", res);
642    return (res);
643}
644
645
646/**
647 * Feed @p input_fifo through ntfsclone (restore) to @p output_device.
648 * @param input_fifo The ntfsclone file to read.
649 * @param output_device Where to put the output.
650 * @return The return value of ntfsclone (0 for success).
651 */
652int feed_outfrom_ntfsprog(char *output_device, char *input_fifo)
653{
654// RESTORE
655    int res = -1;
656    char *command = NULL;
657    char *tmp = NULL;
658
659    tmp = find_home_of_exe("ntfsclone");
660    if (!tmp) {
661        fatal_error("ntfsclone not found");
662    }
663    mr_free(tmp);
664
665    mr_asprintf(&command, "ntfsclone --force --restore-image --overwrite %s %s", output_device, input_fifo);
666    res = run_program_and_log_output(command, 5);
667    mr_free(command);
668    return (res);
669}
Note: See TracBrowser for help on using the repository browser.