/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugins.d/usplash.c

  • Committer: Björn Påhlsson
  • Date: 2008-07-20 02:52:20 UTC
  • Revision ID: belorn@braxen-20080720025220-r5u0388uy9iu23h6
Added following support:
Pluginbased client handler
rewritten Mandos client
       Avahi instead of udp server discovery
       openpgp encrypted key support
Passprompt stand alone application for direct console input
Added logging for Mandos server

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8 -*- */
2
 
/*
3
 
 * Usplash - Read a password from usplash and output it
4
 
 * 
5
 
 * Copyright © 2008,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
7
 
 * 
8
 
 * This program is free software: you can redistribute it and/or
9
 
 * modify it under the terms of the GNU General Public License as
10
 
 * published by the Free Software Foundation, either version 3 of the
11
 
 * License, or (at your option) any later version.
12
 
 * 
13
 
 * This program is distributed in the hope that it will be useful, but
14
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 
 * General Public License for more details.
17
 
 * 
18
 
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
21
 
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
23
 
 */
24
 
 
25
 
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
26
 
#include <signal.h>             /* sig_atomic_t, struct sigaction,
27
 
                                   sigemptyset(), sigaddset(), SIGINT,
28
 
                                   SIGHUP, SIGTERM, sigaction(),
29
 
                                   SIG_IGN, kill(), SIGKILL */
30
 
#include <stdbool.h>            /* bool, false, true */
31
 
#include <fcntl.h>              /* open(), O_WRONLY, O_RDONLY */
32
 
#include <iso646.h>             /* and, or, not*/
33
 
#include <errno.h>              /* errno, EINTR */
34
 
#include <sys/types.h>          /* size_t, ssize_t, pid_t, DIR, struct
35
 
                                   dirent */
36
 
#include <stddef.h>             /* NULL */
37
 
#include <string.h>             /* strlen(), memcmp() */
38
 
#include <stdio.h>              /* asprintf(), perror() */
39
 
#include <unistd.h>             /* close(), write(), readlink(),
40
 
                                   read(), STDOUT_FILENO, sleep(),
41
 
                                   fork(), setuid(), geteuid(),
42
 
                                   setsid(), chdir(), dup2(),
43
 
                                   STDERR_FILENO, execv() */
44
 
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
45
 
                                   EXIT_SUCCESS, malloc(), _exit() */
46
 
#include <stdlib.h>             /* getenv() */
47
 
#include <dirent.h>             /* opendir(), readdir(), closedir() */
48
 
#include <inttypes.h>           /* intmax_t, strtoimax() */
49
 
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK */
50
 
 
51
 
sig_atomic_t interrupted_by_signal = 0;
52
 
int signal_received;
53
 
const char usplash_name[] = "/sbin/usplash";
54
 
 
55
 
static void termination_handler(int signum){
56
 
  if(interrupted_by_signal){
57
 
    return;
58
 
  }
59
 
  interrupted_by_signal = 1;
60
 
  signal_received = signum;
61
 
}
62
 
 
63
 
static bool usplash_write(int *fifo_fd_r,
64
 
                          const char *cmd, const char *arg){
65
 
  /* 
66
 
   * usplash_write(&fd, "TIMEOUT", "15") will write "TIMEOUT 15\0"
67
 
   * usplash_write(&fd, "PULSATE", NULL) will write "PULSATE\0"
68
 
   * SEE ALSO
69
 
   *         usplash_write(8)
70
 
   */
71
 
  int ret;
72
 
  if(*fifo_fd_r == -1){
73
 
    ret = open("/dev/.initramfs/usplash_fifo", O_WRONLY);
74
 
    if(ret == -1){
75
 
      return false;
76
 
    }
77
 
    *fifo_fd_r = ret;
78
 
  }
79
 
  
80
 
  const char *cmd_line;
81
 
  size_t cmd_line_len;
82
 
  char *cmd_line_alloc = NULL;
83
 
  if(arg == NULL){
84
 
    cmd_line = cmd;
85
 
    cmd_line_len = strlen(cmd) + 1;
86
 
  } else {
87
 
    do {
88
 
      ret = asprintf(&cmd_line_alloc, "%s %s", cmd, arg);
89
 
      if(ret == -1){
90
 
        int e = errno;
91
 
        TEMP_FAILURE_RETRY(close(*fifo_fd_r));
92
 
        errno = e;
93
 
        return false;
94
 
      }
95
 
    } while(ret == -1);
96
 
    cmd_line = cmd_line_alloc;
97
 
    cmd_line_len = (size_t)ret + 1;
98
 
  }
99
 
  
100
 
  size_t written = 0;
101
 
  ssize_t sret = 0;
102
 
  while(written < cmd_line_len){
103
 
    sret = write(*fifo_fd_r, cmd_line + written,
104
 
                 cmd_line_len - written);
105
 
    if(sret == -1){
106
 
      int e = errno;
107
 
      TEMP_FAILURE_RETRY(close(*fifo_fd_r));
108
 
      free(cmd_line_alloc);
109
 
      errno = e;
110
 
      return false;
111
 
    }
112
 
    written += (size_t)sret;
113
 
  }
114
 
  free(cmd_line_alloc);
115
 
  
116
 
  return true;
117
 
}
118
 
 
119
 
/* Create prompt string */
120
 
char *makeprompt(void){
121
 
  int ret = 0;
122
 
  char *prompt;
123
 
  const char *const cryptsource = getenv("cryptsource");
124
 
  const char *const crypttarget = getenv("crypttarget");
125
 
  const char prompt_start[] = "Enter passphrase to unlock the disk";
126
 
  
127
 
  if(cryptsource == NULL){
128
 
    if(crypttarget == NULL){
129
 
      ret = asprintf(&prompt, "%s: ", prompt_start);
130
 
    } else {
131
 
      ret = asprintf(&prompt, "%s (%s): ", prompt_start,
132
 
                     crypttarget);
133
 
    }
134
 
  } else {
135
 
    if(crypttarget == NULL){
136
 
      ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
137
 
    } else {
138
 
      ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
139
 
                     cryptsource, crypttarget);
140
 
    }
141
 
  }
142
 
  if(ret == -1){
143
 
    return NULL;
144
 
  }
145
 
  return prompt;
146
 
}
147
 
 
148
 
pid_t find_usplash(char **cmdline_r, size_t *cmdline_len_r){
149
 
  int ret = 0;
150
 
  ssize_t sret = 0;
151
 
  char *cmdline = NULL;
152
 
  size_t cmdline_len = 0;
153
 
  DIR *proc_dir = opendir("/proc");
154
 
  if(proc_dir == NULL){
155
 
    perror("opendir");
156
 
    return -1;
157
 
  }
158
 
  errno = 0;
159
 
  for(struct dirent *proc_ent = readdir(proc_dir);
160
 
      proc_ent != NULL;
161
 
      proc_ent = readdir(proc_dir)){
162
 
    pid_t pid;
163
 
    {
164
 
      intmax_t tmpmax;
165
 
      char *tmp;
166
 
      tmpmax = strtoimax(proc_ent->d_name, &tmp, 10);
167
 
      if(errno != 0 or tmp == proc_ent->d_name or *tmp != '\0'
168
 
         or tmpmax != (pid_t)tmpmax){
169
 
        /* Not a process */
170
 
        errno = 0;
171
 
        continue;
172
 
      }
173
 
      pid = (pid_t)tmpmax;
174
 
    }
175
 
    /* Find the executable name by doing readlink() on the
176
 
       /proc/<pid>/exe link */
177
 
    char exe_target[sizeof(usplash_name)];
178
 
    {
179
 
      /* create file name string */
180
 
      char *exe_link;
181
 
      ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
182
 
      if(ret == -1){
183
 
        perror("asprintf");
184
 
        goto fail_find_usplash;
185
 
      }
186
 
      
187
 
      /* Check that it refers to a symlink owned by root:root */
188
 
      struct stat exe_stat;
189
 
      ret = lstat(exe_link, &exe_stat);
190
 
      if(ret == -1){
191
 
        if(errno == ENOENT){
192
 
          free(exe_link);
193
 
          continue;
194
 
        }
195
 
        perror("lstat");
196
 
        free(exe_link);
197
 
        goto fail_find_usplash;
198
 
      }
199
 
      if(not S_ISLNK(exe_stat.st_mode)
200
 
         or exe_stat.st_uid != 0
201
 
         or exe_stat.st_gid != 0){
202
 
        free(exe_link);
203
 
        continue;
204
 
      }
205
 
        
206
 
      sret = readlink(exe_link, exe_target, sizeof(exe_target));
207
 
      free(exe_link);
208
 
    }
209
 
    /* Compare executable name */
210
 
    if((sret != ((ssize_t)sizeof(exe_target)-1))
211
 
       or (memcmp(usplash_name, exe_target,
212
 
                  sizeof(exe_target)-1) != 0)){
213
 
      /* Not it */
214
 
      continue;
215
 
    }
216
 
    /* Found usplash */
217
 
    /* Read and save the command line of usplash in "cmdline" */
218
 
    {
219
 
      /* Open /proc/<pid>/cmdline  */
220
 
      int cl_fd;
221
 
      {
222
 
        char *cmdline_filename;
223
 
        ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
224
 
                       proc_ent->d_name);
225
 
        if(ret == -1){
226
 
          perror("asprintf");
227
 
          goto fail_find_usplash;
228
 
        }
229
 
        cl_fd = open(cmdline_filename, O_RDONLY);
230
 
        free(cmdline_filename);
231
 
        if(cl_fd == -1){
232
 
          perror("open");
233
 
          goto fail_find_usplash;
234
 
        }
235
 
      }
236
 
      size_t cmdline_allocated = 0;
237
 
      char *tmp;
238
 
      const size_t blocksize = 1024;
239
 
      do {
240
 
        /* Allocate more space? */
241
 
        if(cmdline_len + blocksize > cmdline_allocated){
242
 
          tmp = realloc(cmdline, cmdline_allocated + blocksize);
243
 
          if(tmp == NULL){
244
 
            perror("realloc");
245
 
            close(cl_fd);
246
 
            goto fail_find_usplash;
247
 
          }
248
 
          cmdline = tmp;
249
 
          cmdline_allocated += blocksize;
250
 
        }
251
 
        /* Read data */
252
 
        sret = read(cl_fd, cmdline + cmdline_len,
253
 
                    cmdline_allocated - cmdline_len);
254
 
        if(sret == -1){
255
 
          perror("read");
256
 
          close(cl_fd);
257
 
          goto fail_find_usplash;
258
 
        }
259
 
        cmdline_len += (size_t)sret;
260
 
      } while(sret != 0);
261
 
      ret = close(cl_fd);
262
 
      if(ret == -1){
263
 
        perror("close");
264
 
        goto fail_find_usplash;
265
 
      }
266
 
    }
267
 
    /* Close directory */
268
 
    ret = closedir(proc_dir);
269
 
    if(ret == -1){
270
 
      perror("closedir");
271
 
      goto fail_find_usplash;
272
 
    }
273
 
    /* Success */
274
 
    *cmdline_r = cmdline;
275
 
    *cmdline_len_r = cmdline_len;
276
 
    return pid;
277
 
  }
278
 
  
279
 
 fail_find_usplash:
280
 
  
281
 
  free(cmdline);
282
 
  if(proc_dir != NULL){
283
 
    int e = errno;
284
 
    closedir(proc_dir);
285
 
    errno = e;
286
 
  }
287
 
  return 0;
288
 
}
289
 
 
290
 
int main(__attribute__((unused))int argc,
291
 
         __attribute__((unused))char **argv){
292
 
  int ret = 0;
293
 
  ssize_t sret;
294
 
  int fifo_fd = -1;
295
 
  int outfifo_fd = -1;
296
 
  char *buf = NULL;
297
 
  size_t buf_len = 0;
298
 
  pid_t usplash_pid = -1;
299
 
  bool usplash_accessed = false;
300
 
  
301
 
  char *prompt = makeprompt();
302
 
  if(prompt == NULL){
303
 
    goto failure;
304
 
  }
305
 
  
306
 
  /* Find usplash process */
307
 
  char *cmdline = NULL;
308
 
  size_t cmdline_len = 0;
309
 
  usplash_pid = find_usplash(&cmdline, &cmdline_len);
310
 
  if(usplash_pid == 0){
311
 
    goto failure;
312
 
  }
313
 
  
314
 
  /* Set up the signal handler */
315
 
  {
316
 
    struct sigaction old_action,
317
 
      new_action = { .sa_handler = termination_handler,
318
 
                     .sa_flags = 0 };
319
 
    sigemptyset(&new_action.sa_mask);
320
 
    ret = sigaddset(&new_action.sa_mask, SIGINT);
321
 
    if(ret == -1){
322
 
      perror("sigaddset");
323
 
      goto failure;
324
 
    }
325
 
    ret = sigaddset(&new_action.sa_mask, SIGHUP);
326
 
    if(ret == -1){
327
 
      perror("sigaddset");
328
 
      goto failure;
329
 
    }
330
 
    ret = sigaddset(&new_action.sa_mask, SIGTERM);
331
 
    if(ret == -1){
332
 
      perror("sigaddset");
333
 
      goto failure;
334
 
    }
335
 
    ret = sigaction(SIGINT, NULL, &old_action);
336
 
    if(ret == -1){
337
 
      if(errno != EINTR){
338
 
        perror("sigaction");
339
 
      }
340
 
      goto failure;
341
 
    }
342
 
    if(old_action.sa_handler != SIG_IGN){
343
 
      ret = sigaction(SIGINT, &new_action, NULL);
344
 
      if(ret == -1){
345
 
        if(errno != EINTR){
346
 
          perror("sigaction");
347
 
        }
348
 
        goto failure;
349
 
      }
350
 
    }
351
 
    ret = sigaction(SIGHUP, NULL, &old_action);
352
 
    if(ret == -1){
353
 
      if(errno != EINTR){
354
 
        perror("sigaction");
355
 
      }
356
 
      goto failure;
357
 
    }
358
 
    if(old_action.sa_handler != SIG_IGN){
359
 
      ret = sigaction(SIGHUP, &new_action, NULL);
360
 
      if(ret == -1){
361
 
        if(errno != EINTR){
362
 
          perror("sigaction");
363
 
        }
364
 
        goto failure;
365
 
      }
366
 
    }
367
 
    ret = sigaction(SIGTERM, NULL, &old_action);
368
 
    if(ret == -1){
369
 
      if(errno != EINTR){
370
 
        perror("sigaction");
371
 
      }
372
 
      goto failure;
373
 
    }
374
 
    if(old_action.sa_handler != SIG_IGN){
375
 
      ret = sigaction(SIGTERM, &new_action, NULL);
376
 
      if(ret == -1){
377
 
        if(errno != EINTR){
378
 
          perror("sigaction");
379
 
        }
380
 
        goto failure;
381
 
      }
382
 
    }
383
 
  }
384
 
  
385
 
  usplash_accessed = true;
386
 
  /* Write command to FIFO */
387
 
  if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){
388
 
    if(errno != EINTR){
389
 
      perror("usplash_write");
390
 
    }
391
 
    goto failure;
392
 
  }
393
 
  
394
 
  if(interrupted_by_signal){
395
 
    goto failure;
396
 
  }
397
 
  
398
 
  if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){
399
 
    if(errno != EINTR){
400
 
      perror("usplash_write");
401
 
    }
402
 
    goto failure;
403
 
  }
404
 
  
405
 
  if(interrupted_by_signal){
406
 
    goto failure;
407
 
  }
408
 
  
409
 
  free(prompt);
410
 
  prompt = NULL;
411
 
  
412
 
  /* Read reply from usplash */
413
 
  /* Open FIFO */
414
 
  outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY);
415
 
  if(outfifo_fd == -1){
416
 
    if(errno != EINTR){
417
 
      perror("open");
418
 
    }
419
 
    goto failure;
420
 
  }
421
 
  
422
 
  if(interrupted_by_signal){
423
 
    goto failure;
424
 
  }
425
 
  
426
 
  /* Read from FIFO */
427
 
  size_t buf_allocated = 0;
428
 
  const size_t blocksize = 1024;
429
 
  do {
430
 
    /* Allocate more space */
431
 
    if(buf_len + blocksize > buf_allocated){
432
 
      char *tmp = realloc(buf, buf_allocated + blocksize);
433
 
      if(tmp == NULL){
434
 
        if(errno != EINTR){
435
 
          perror("realloc");
436
 
        }
437
 
        goto failure;
438
 
      }
439
 
      buf = tmp;
440
 
      buf_allocated += blocksize;
441
 
    }
442
 
    sret = read(outfifo_fd, buf + buf_len,
443
 
                buf_allocated - buf_len);
444
 
    if(sret == -1){
445
 
      if(errno != EINTR){
446
 
        perror("read");
447
 
      }
448
 
      TEMP_FAILURE_RETRY(close(outfifo_fd));
449
 
      goto failure;
450
 
    }
451
 
    if(interrupted_by_signal){
452
 
      break;
453
 
    }
454
 
    
455
 
    buf_len += (size_t)sret;
456
 
  } while(sret != 0);
457
 
  ret = close(outfifo_fd);
458
 
  if(ret == -1){
459
 
    if(errno != EINTR){
460
 
      perror("close");
461
 
    }
462
 
    goto failure;
463
 
  }
464
 
  outfifo_fd = -1;
465
 
  
466
 
  if(interrupted_by_signal){
467
 
    goto failure;
468
 
  }
469
 
  
470
 
  if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){
471
 
    if(errno != EINTR){
472
 
      perror("usplash_write");
473
 
    }
474
 
    goto failure;
475
 
  }
476
 
  
477
 
  if(interrupted_by_signal){
478
 
    goto failure;
479
 
  }
480
 
  
481
 
  ret = close(fifo_fd);
482
 
  if(ret == -1){
483
 
    if(errno != EINTR){
484
 
      perror("close");
485
 
    }
486
 
    goto failure;
487
 
  }
488
 
  fifo_fd = -1;
489
 
  
490
 
  /* Print password to stdout */
491
 
  size_t written = 0;
492
 
  while(written < buf_len){
493
 
    do {
494
 
      sret = write(STDOUT_FILENO, buf + written, buf_len - written);
495
 
      if(sret == -1){
496
 
        if(errno != EINTR){
497
 
          perror("write");
498
 
        }
499
 
        goto failure;
500
 
      }
501
 
    } while(sret == -1);
502
 
    
503
 
    if(interrupted_by_signal){
504
 
      goto failure;
505
 
    }
506
 
    written += (size_t)sret;
507
 
  }
508
 
  free(buf);
509
 
  buf = NULL;
510
 
  
511
 
  if(interrupted_by_signal){
512
 
    goto failure;
513
 
  }
514
 
  
515
 
  free(cmdline);
516
 
  return EXIT_SUCCESS;
517
 
  
518
 
 failure:
519
 
  
520
 
  free(buf);
521
 
  
522
 
  free(prompt);
523
 
  
524
 
  /* If usplash was never accessed, we can stop now */
525
 
  if(not usplash_accessed){
526
 
    return EXIT_FAILURE;
527
 
  }
528
 
  
529
 
  /* Close FIFO */
530
 
  if(fifo_fd != -1){
531
 
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
532
 
    if(ret == -1 and errno != EINTR){
533
 
      perror("close");
534
 
    }
535
 
    fifo_fd = -1;
536
 
  }
537
 
  
538
 
  /* Close output FIFO */
539
 
  if(outfifo_fd != -1){
540
 
    ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd));
541
 
    if(ret == -1){
542
 
      perror("close");
543
 
    }
544
 
  }
545
 
  
546
 
  /* Create argc and argv for new usplash*/
547
 
  int cmdline_argc = 0;
548
 
  char **cmdline_argv = malloc(sizeof(char *));
549
 
  {
550
 
    size_t position = 0;
551
 
    while(position < cmdline_len){
552
 
      char **tmp = realloc(cmdline_argv,
553
 
                           (sizeof(char *)
554
 
                            * (size_t)(cmdline_argc + 2)));
555
 
      if(tmp == NULL){
556
 
        perror("realloc");
557
 
        free(cmdline_argv);
558
 
        return EXIT_FAILURE;
559
 
      }
560
 
      cmdline_argv = tmp;
561
 
      cmdline_argv[cmdline_argc] = cmdline + position;
562
 
      cmdline_argc++;
563
 
      position += strlen(cmdline + position) + 1;
564
 
    }
565
 
    cmdline_argv[cmdline_argc] = NULL;
566
 
  }
567
 
  /* Kill old usplash */
568
 
  kill(usplash_pid, SIGTERM);
569
 
  sleep(2);
570
 
  while(kill(usplash_pid, 0) == 0){
571
 
    kill(usplash_pid, SIGKILL);
572
 
    sleep(1);
573
 
  }
574
 
  
575
 
  pid_t new_usplash_pid = fork();
576
 
  if(new_usplash_pid == 0){
577
 
    /* Child; will become new usplash process */
578
 
    
579
 
    /* Make the effective user ID (root) the only user ID instead of
580
 
       the real user ID (_mandos) */
581
 
    ret = setuid(geteuid());
582
 
    if(ret == -1){
583
 
      perror("setuid");
584
 
    }
585
 
    
586
 
    setsid();
587
 
    ret = chdir("/");
588
 
/*     if(fork() != 0){ */
589
 
/*       _exit(EXIT_SUCCESS); */
590
 
/*     } */
591
 
    ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
592
 
    if(ret == -1){
593
 
      perror("dup2");
594
 
      _exit(EXIT_FAILURE);
595
 
    }
596
 
    
597
 
    execv(usplash_name, cmdline_argv);
598
 
    if(not interrupted_by_signal){
599
 
      perror("execv");
600
 
    }
601
 
    free(cmdline);
602
 
    free(cmdline_argv);
603
 
    _exit(EXIT_FAILURE);
604
 
  }
605
 
  free(cmdline);
606
 
  free(cmdline_argv);
607
 
  sleep(2);
608
 
  if(not usplash_write(&fifo_fd, "PULSATE", NULL)){
609
 
    if(errno != EINTR){
610
 
      perror("usplash_write");
611
 
    }
612
 
  }
613
 
  
614
 
  /* Close FIFO (again) */
615
 
  if(fifo_fd != -1){
616
 
    ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
617
 
    if(ret == -1 and errno != EINTR){
618
 
      perror("close");
619
 
    }
620
 
    fifo_fd = -1;
621
 
  }
622
 
  
623
 
  if(interrupted_by_signal){
624
 
    struct sigaction signal_action = { .sa_handler = SIG_DFL };
625
 
    sigemptyset(&signal_action.sa_mask);
626
 
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
627
 
                                            &signal_action, NULL));
628
 
    if(ret == -1){
629
 
      perror("sigaction");
630
 
    }
631
 
    do {
632
 
      ret = raise(signal_received);
633
 
    } while(ret != 0 and errno == EINTR);
634
 
    if(ret != 0){
635
 
      perror("raise");
636
 
      abort();
637
 
    }
638
 
    TEMP_FAILURE_RETRY(pause());
639
 
  }
640
 
  
641
 
  return EXIT_FAILURE;
642
 
}