source: MondoRescue/branches/stable/mondo/src/common/libmondo-fork.c@ 1140

Last change on this file since 1140 was 1140, checked in by Bruno Cornec, 17 years ago

Try to fix some valgrind reports (note that this version still doesn't work)

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