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

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

Some merges from stable (synchro for mem. mngt)

  • Property svn:keywords set to Id
File size: 18.1 KB
Line 
1/* libmondo-fork.c - subroutines for handling forking/pthreads/etc.
2 * $Id: libmondo-fork.c 1176 2007-02-16 13:16:14Z bruno $
3 */
4#include "my-stuff.h"
5#include "mr_mem.h"
6#include "mr_msg.h"
7#include "mr_str.h"
8
9#include "mondostructures.h"
10#include "libmondo-fork.h"
11#include "libmondo-string-EXT.h"
12#include "newt-specific-EXT.h"
13#include "libmondo-files-EXT.h"
14#include "libmondo-tools-EXT.h"
15
16extern bool g_text_mode;
17pid_t g_buffer_pid = 0;
18
19
20/**
21 * Call a program and retrieve its last line of output.
22 * @param call The program to run.
23 * @return The last line of its output.
24 * @note The returned value should be freed by caller
25 */
26char *call_program_and_get_last_line_of_output(char *call)
27{
28    char *result = NULL;
29    FILE *fin = NULL;
30    size_t n = 0;
31
32    assert_string_is_neither_NULL_nor_zerolength(call);
33
34    if ((fin = popen(call, "r"))) {
35        for (mr_getline(&result, &n, fin); !feof(fin); mr_getline(&result, &n, fin));
36        paranoid_pclose(fin);
37    } else {
38        log_OS_error("Unable to popen call");
39    }
40    return(result);
41}
42
43#define MONDO_POPMSG  _("Your PC will not retract the CD tray automatically. Please call mondoarchive with the -m (manual CD tray) flag.")
44
45/**
46 * Call mkisofs to create an ISO image.
47 * @param bkpinfo The backup information structure. Fields used:
48 * - @c bkpinfo->manual_cd_tray
49 * - @c bkpinfo->backup_media_type
50 * - @c bkpinfo->please_dont_eject_when_restoring
51 * @param basic_call The call to mkisofs. May contain tokens that will be resolved to actual data. The tokens are:
52 * - @c _ISO_ will become the ISO file (@p isofile)
53 * - @c _CD#_ becomes the CD number (@p cd_no)
54 * - @c _ERR_ becomes the logfile (@p g_logfile)
55 * @param isofile Replaces @c _ISO_ in @p basic_call. Should probably be the ISO image to create (-o parameter to mkisofs).
56 * @param cd_no Replaces @c _CD#_ in @p basic_call. Should probably be the CD number.
57 * @param logstub Unused.
58 * @param what_i_am_doing The action taking place (e.g. "Making ISO #1"). Used as the title of the progress dialog.
59 * @return Exit code of @c mkisofs (0 is success, anything else indicates failure).
60 * @bug @p logstub is unused.
61 */
62int
63eval_call_to_make_ISO(struct s_bkpinfo *bkpinfo,
64                      char *basic_call, char *isofile,
65                      int cd_no, char *logstub, char *what_i_am_doing)
66{
67
68    /*@ int's  *** */
69    int retval = 0;
70
71
72    /*@ buffers      *** */
73    char *midway_call, *ultimate_call, *tmp;
74    char *p = NULL; 
75    char *cd_number_str = NULL;
76    char *command = NULL;
77   
78
79/*@***********   End Variables ***************************************/
80
81    mr_msg(3, "Starting");
82    assert(bkpinfo != NULL);
83    assert_string_is_neither_NULL_nor_zerolength(basic_call);
84    assert_string_is_neither_NULL_nor_zerolength(isofile);
85    assert_string_is_neither_NULL_nor_zerolength(logstub);
86
87    midway_call = mr_malloc(1200);
88    ultimate_call = mr_malloc(1200);
89    tmp = mr_malloc(1200);
90
91    mr_asprintf(&cd_number_str, "%d", cd_no);
92    resolve_naff_tokens(midway_call, basic_call, isofile, "_ISO_");
93    resolve_naff_tokens(tmp, midway_call, cd_number_str, "_CD#_");
94    mr_free(cd_number_str);
95
96    resolve_naff_tokens(ultimate_call, tmp, MONDO_LOGFILE, "_ERR_");
97    mr_msg(4, "basic call = '%s'", basic_call);
98    mr_msg(4, "midway_call = '%s'", midway_call);
99    mr_msg(4, "tmp = '%s'", tmp);
100    mr_msg(4, "ultimate call = '%s'", ultimate_call);
101    mr_asprintf(&command, "%s >> %s", ultimate_call, MONDO_LOGFILE);
102
103    log_to_screen
104        (_("Please be patient. Do not be alarmed by on-screen inactivity."));
105    mr_msg(4, "Calling open_evalcall_form() with what_i_am_doing='%s'",
106            what_i_am_doing);
107    if (bkpinfo->manual_cd_tray) {
108        /* Find everything after a 2>> and remove it */
109        p = strstr(command, "2>>");
110        if (p) {
111            for (; *p != ' ' && *p != '\0'; p++) {
112                *p = ' ';
113            }
114        }
115#ifndef _XWIN
116        if (!g_text_mode) {
117            newtSuspend();
118        }
119#endif
120        mr_msg(1, "command = '%s'", command);
121        retval += system(command);
122        if (!g_text_mode) {
123            newtResume();
124        }
125        if (retval) {
126            mr_msg(2, "Basic call '%s' returned an error.", basic_call);
127            popup_and_OK(_("Press ENTER to continue."));
128            popup_and_OK
129                (_("mkisofs and/or cdrecord returned an error. CD was not created"));
130        }
131    }
132    /* if text mode then do the above & RETURN; if not text mode, do this... */
133    else {
134        mr_msg(3, "command = '%s'", command);
135//      yes_this_is_a_goto:
136        retval =
137            run_external_binary_with_percentage_indicator_NEW
138            (what_i_am_doing, command);
139    }
140    mr_free(command);
141
142    mr_free(midway_call);
143    mr_free(ultimate_call);
144    mr_free(tmp);
145    return (retval);
146}
147
148
149/**
150 * Run a program and log its output (stdout and stderr) to the logfile.
151 * @param program The program to run. Passed to the shell, so you can use pipes etc.
152 * @param debug_level If @p g_loglevel is higher than this, do not log the output.
153 * @return The exit code of @p program (depends on the command, but 0 almost always indicates success).
154 */
155int run_program_and_log_output(char *program, int debug_level)
156{
157    /*@ buffer ****************************************************** */
158    char *callstr = NULL;
159    char *incoming = NULL;
160
161    /*@ int ********************************************************* */
162    int res = 0;
163    size_t n = 0;
164    bool log_if_failure = FALSE;
165    bool log_if_success = FALSE;
166
167    /*@ pointers *************************************************** */
168    FILE *fin = NULL;
169    char *p = NULL;
170
171    /*@ end vars *************************************************** */
172
173    assert(program != NULL);
174    if (!program[0]) {
175        mr_msg(2, "Warning - asked to run zerolength program");
176        return (1);
177    }
178
179    if (debug_level <= g_loglevel) {
180        log_if_success = TRUE;
181        log_if_failure = TRUE;
182    }
183    mr_asprintf(&callstr,
184            "%s > /tmp/mondo-run-prog-thing.tmp 2> /tmp/mondo-run-prog-thing.err",
185            program);
186    while ((p = strchr(callstr, '\r'))) {
187        *p = ' ';
188    }
189    while ((p = strchr(callstr, '\n'))) {
190        *p = ' ';
191    }                           /* single '=' is intentional */
192
193    res = system(callstr);
194    if (((res == 0) && log_if_success) || ((res != 0) && log_if_failure)) {
195        mr_msg(0, "running: %s", callstr);
196        mr_msg(0,
197                "--------------------------------start of output-----------------------------");
198    }
199    mr_free(callstr);
200
201    if (log_if_failure
202        &&
203        system
204        ("cat /tmp/mondo-run-prog-thing.err >> /tmp/mondo-run-prog-thing.tmp 2> /dev/null"))
205    {
206        log_OS_error("Command failed");
207    }
208    unlink("/tmp/mondo-run-prog-thing.err");
209    fin = fopen("/tmp/mondo-run-prog-thing.tmp", "r");
210    if (fin) {
211        for (mr_getline(&incoming, &n, fin); !feof(fin); mr_getline(&incoming, &n, fin)) {
212            /* patch by Heiko Schlittermann */
213            p = incoming;
214            while (p && *p) {
215                if ((p = strchr(p, '%'))) {
216                    memmove(p, p + 1, strlen(p) + 1);
217                    p += 2;
218                }
219            }
220            /* end of patch */
221            mr_strip_spaces(incoming);
222            if ((res == 0 && log_if_success)
223                || (res != 0 && log_if_failure)) {
224                mr_msg(0, incoming);
225            }
226        }
227        mr_free(incoming);
228        paranoid_fclose(fin);
229    }
230    unlink("/tmp/mondo-run-prog-thing.tmp");
231    if ((res == 0 && log_if_success) || (res != 0 && log_if_failure)) {
232        mr_msg(0,
233                "--------------------------------end of output------------------------------");
234        if (res) {
235            mr_msg(0, "...ran with res=%d", res);
236        } else {
237            mr_msg(0, "...ran just fine. :-)");
238        }
239    }
240    return (res);
241}
242
243
244/**
245 * Run a program and log its output to the screen.
246 * @param basic_call The program to run.
247 * @param what_i_am_doing The title of the evalcall form.
248 * @return The return value of the command (varies, but 0 almost always means success).
249 * @see run_program_and_log_output
250 * @see log_to_screen
251 */
252int run_program_and_log_to_screen(char *basic_call, char *what_i_am_doing)
253{
254    /*@ int ******************************************************** */
255    int retval = 0;
256    int res = 0;
257    int i;
258
259    /*@ pointers **************************************************** */
260    FILE *fin;
261
262    /*@ buffers **************************************************** */
263    char *tmp = NULL;
264    char *command = NULL;
265    char *lockfile = NULL;
266
267    /*@ end vars *************************************************** */
268
269    assert_string_is_neither_NULL_nor_zerolength(basic_call);
270
271    mr_asprintf(&lockfile, "/tmp/mojo-jojo.blah.XXXXXX");
272    mkstemp(lockfile);
273    mr_asprintf(&command,
274            "echo hi > %s ; %s >> %s 2>> %s; res=$?; sleep 1; rm -f %s; exit $res",
275            lockfile, basic_call, MONDO_LOGFILE, MONDO_LOGFILE, lockfile);
276    open_evalcall_form(what_i_am_doing);
277    mr_asprintf(&tmp, "Executing %s", basic_call);
278    mr_msg(2, tmp);
279    mr_free(tmp);
280
281    if (!(fin = popen(command, "r"))) {
282        log_OS_error("Unable to popen-in command");
283        mr_asprintf(&tmp, _("Failed utterly to call '%s'"), command);
284        log_to_screen(tmp);
285        mr_free(tmp);
286        mr_free(lockfile);
287        mr_free(command);
288        return (1);
289    }
290    mr_free(command);
291
292    if (!does_file_exist(lockfile)) {
293        log_to_screen(_("Waiting for external binary to start"));
294        for (i = 0; i < 60 && !does_file_exist(lockfile); sleep(1), i++) {
295            mr_msg(3, "Waiting for lockfile %s to exist", lockfile);
296        }
297    }
298    /* This works on Newt, and it gives quicker updates. */
299    for (; does_file_exist(lockfile); sleep(1)) {
300        log_file_end_to_screen(MONDO_LOGFILE, "");
301        update_evalcall_form(1);
302    }
303    /* Evaluate the status returned by pclose to get the exit code of the called program. */
304    errno = 0;
305    res = pclose(fin);
306    /* Log actual pclose errors. */
307    if (errno) {
308        mr_msg(5, "pclose err: %d", errno);
309    }
310    /* Check if we have a valid status. If we do, extract the called program's exit code. */
311    /* If we don't, highlight this fact by returning -1. */
312    if (WIFEXITED(res)) {
313        retval = WEXITSTATUS(res);
314    } else {
315        retval = -1;
316    }
317    close_evalcall_form();
318    unlink(lockfile);
319    mr_free(lockfile);
320
321    return (retval);
322}
323
324
325/**
326 * Apparently used. @bug This has a purpose, but what?
327 */
328#define PIMP_START_SZ "STARTSTARTSTART9ff3kff9a82gv34r7fghbkaBBC2T231hc81h42vws8"
329#define PIMP_END_SZ "ENDENDEND0xBBC10xBBC2T231hc81h42vws89ff3kff9a82gv34r7fghbka"
330
331
332
333
334int copy_from_src_to_dest(FILE * f_orig, FILE * f_archived, char direction)
335{
336// if dir=='w' then copy from orig to archived
337// if dir=='r' then copy from archived to orig
338    char *tmp = NULL;
339    char *tmp1 = NULL;
340    char *buf = NULL;
341    long int bytes_to_be_read, bytes_read_in, bytes_written_out =
342        0, bufcap, subsliceno = 0;
343    int retval = 0;
344    FILE *fin = NULL;
345    FILE *fout = NULL;
346    FILE *ftmp = NULL;
347
348    mr_msg(5, "Opening.");
349    bufcap = 256L * 1024L;
350    buf = mr_malloc(bufcap);
351
352    if (direction == 'w') {
353        fin = f_orig;
354        fout = f_archived;
355        mr_asprintf(&tmp1, "%-64s", PIMP_START_SZ);
356        if (fwrite(tmp1, 1, 64, fout) != 64) {
357            fatal_error("Can't write the introductory block");
358        }
359        mr_free(tmp1);
360
361        while (1) {
362            bytes_to_be_read = bytes_read_in = fread(buf, 1, bufcap, fin);
363            if (bytes_read_in == 0) {
364                break;
365            }
366            mr_asprintf(&tmp1, "%-64ld", bytes_read_in);
367            if (fwrite(tmp1, 1, 64, fout) != 64) {
368                fatal_error("Cannot write introductory block");
369            }
370            mr_free(tmp1);
371
372            mr_msg(7,
373                    "subslice #%ld --- I have read %ld of %ld bytes in from f_orig",
374                    subsliceno, bytes_read_in, bytes_to_be_read);
375            bytes_written_out += fwrite(buf, 1, bytes_read_in, fout);
376            mr_asprintf(&tmp1, "%-64ld", subsliceno);
377            if (fwrite(tmp1, 1, 64, fout) != 64) {
378                fatal_error("Cannot write post-thingy block");
379            }
380            mr_free(tmp1);
381
382            mr_msg(7, "Subslice #%d written OK", subsliceno);
383            subsliceno++;
384        }
385        mr_asprintf(&tmp, "%-64ld", 0L);
386        if (fwrite(tmp, 1, 64L, fout) != 64L) {
387            fatal_error("Cannot write final introductory block");
388        }
389    } else {
390        fin = f_archived;
391        fout = f_orig;
392        tmp = mr_malloc(64L);
393        if (fread(tmp, 1, 64L, fin) != 64L) {
394            fatal_error("Cannot read the introductory block");
395        }
396        mr_msg(5, "tmp is %s", tmp);
397        if (!strstr(tmp, PIMP_START_SZ)) {
398            fatal_error("Can't find intro blk");
399        }
400        if (fread(tmp, 1, 64L, fin) != 64L) {
401            fatal_error("Cannot read introductory blk");
402        }
403        bytes_to_be_read = atol(tmp);
404        while (bytes_to_be_read > 0) {
405            mr_msg(7, "subslice#%ld, bytes=%ld", subsliceno,
406                    bytes_to_be_read);
407            bytes_read_in = fread(buf, 1, bytes_to_be_read, fin);
408            if (bytes_read_in != bytes_to_be_read) {
409                fatal_error
410                    ("Danger, WIll Robinson. Failed to read whole subvol from archives.");
411            }
412            bytes_written_out += fwrite(buf, 1, bytes_read_in, fout);
413            if (fread(tmp, 1, 64L, fin) != 64L) {
414                fatal_error("Cannot read post-thingy block");
415            }
416            if (atol(tmp) != subsliceno) {
417                mr_msg(1, "Wanted subslice %ld but got %ld ('%s')",
418                        subsliceno, atol(tmp), tmp);
419            }
420            mr_msg(7, "Subslice #%ld read OK", subsliceno);
421            subsliceno++;
422            if (fread(tmp, 1, 64L, fin) != 64L) {
423                fatal_error("Cannot read introductory block");
424            }
425            bytes_to_be_read = atol(tmp);
426        }
427    }
428
429//  mr_msg(4, "Written %ld of %ld bytes", bytes_written_out, bytes_read_in);
430
431    if (direction == 'w') {
432        mr_asprintf(&tmp1, "%-64s", PIMP_END_SZ);
433        if (fwrite(tmp1, 1, 64L, fout) != 64L) {
434            fatal_error("Can't write the final block");
435        }
436        mr_free(tmp1);
437    } else {
438        mr_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            mr_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                mr_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                mr_msg(0, tmp);
458                */
459                fread(tmp, 1, 512, fin);
460                mr_msg(0, "tmp = '%s'", tmp);
461                fwrite(tmp, 1, 512, ftmp);
462                fclose(ftmp);
463                fatal_error("Missing terminating block");
464            }
465        }
466    }
467
468    mr_free(buf);
469    mr_free(tmp);
470    mr_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    mr_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            mr_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    mr_msg(4, "sz_command = '%s'", sz_command);
570    res = system(sz_command);
571    if (res > 256 && res != 4444) {
572        res = res / 256;
573    }
574    mr_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 = NULL;
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    mr_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    mr_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            mr_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    mr_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.