Adding casper 1.59+debian-1.
[live-boot-grml.git] / casper-md5check / casper-md5check.c
1 /* casper-md5check - a tool to check md5sums and talk to usplash
2    (C) Canonical Ltd 2006
3    Written by Tollef Fog Heen <tfheen@ubuntu.com>
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9    
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14    
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18    USA. */
19
20 #define _GNU_SOURCE
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <sys/reboot.h>
24 #include <linux/reboot.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <stdarg.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <math.h>
33 #include <termios.h>
34
35 #define USPLASH_FIFO "/dev/.initramfs/usplash_fifo"
36 #define MAXTRIES 5
37 #include "md5.h"
38 #define DEBUG
39
40 int write_and_retry(int fd, char *s) {
41   int try = 0, ret = 0;
42   char *end;
43
44 #ifdef DEBUG
45   fprintf(stderr, "-> %s\n", s);
46 #endif
47
48   end = s + strlen(s)+1;
49
50   ret = write(fd, s, end - s);
51   while (s + ret < end && try < MAXTRIES) {
52     sleep(1);
53     s += ret;
54     ret = write(fd, s, strlen(s)+1);
55     try++;
56   }
57   return (s+ret == end ? 0 : 1);
58 }
59
60 void usplash_timeout(int fd, int timeout) {
61   char *s;
62
63   asprintf(&s, "TIMEOUT %d", timeout);
64
65   write_and_retry(fd, s);
66
67   free(s);
68
69 }
70
71 void usplash_failure(int fd, char *format, ...) {
72   char *s, *s1;
73   va_list argp;
74
75   va_start(argp, format);
76   vasprintf(&s, format, argp);
77   va_end(argp);
78
79   asprintf(&s1, "FAILURE %s", s);
80
81   write_and_retry(fd, s1);
82
83   free(s);
84   free(s1);
85 }
86
87 void usplash_text(int fd, char *format, ...) {
88   char *s, *s1;
89   va_list argp;
90
91   va_start(argp, format);
92   vasprintf(&s, format, argp);
93   va_end(argp);
94
95   asprintf(&s1, "TEXT %s", s);
96
97   write_and_retry(fd, s1);
98
99   free(s);
100   free(s1);
101 }
102
103 void usplash_success(int fd, char *format, ...) {
104   char *s, *s1;
105   va_list argp;
106
107   va_start(argp, format);
108   vasprintf(&s, format, argp);
109   va_end(argp);
110
111   asprintf(&s1, "SUCCESS %s", s);
112
113   write_and_retry(fd, s1);
114   
115   free(s);
116   free(s1);
117 }
118
119 void usplash_progress(int fd, int progress) {
120   static int prevprogress = -1;
121   char *s;
122
123   if (progress == prevprogress)
124     return;
125   prevprogress = progress;
126
127   asprintf(&s, "PROGRESS %d", progress);
128
129   write_and_retry(fd, s);
130
131   free(s);
132 }
133
134 int set_nocanonical_tty(int fd) {
135   struct termios t;
136
137   if (tcgetattr(fd, &t) == -1) {
138     perror("tcgetattr");
139   }
140   t.c_lflag &= ~ICANON;
141   t.c_cc[VMIN] = 1;
142   t.c_cc[VTIME] = 0;
143   return tcsetattr(fd, TCSANOW, &t);
144 }
145
146 int main(int argc, char **argv) {
147   
148   int pipe_fd, check_fd;
149   int failed = 0;
150   
151   FILE *md5_file;
152   md5_state_t state;
153   md5_byte_t digest[16];
154   char hex_output[16*2 + 1];
155   char *checksum, *checkfile;
156   ssize_t tsize, csize;
157
158   tsize = 0;
159   csize = 0;
160
161   if (argc != 3) {
162     fprintf(stderr,"Wrong number of arguments\n");
163     fprintf(stderr,"%s <root directory> <md5sum file>\n", argv[0]);
164     exit(1);
165   }
166   
167   if (chdir(argv[1]) != 0) {
168     perror("chdir");
169     exit(1);
170   }
171   
172   pipe_fd = open(USPLASH_FIFO, O_WRONLY|O_NONBLOCK);
173   
174   if (pipe_fd == -1) {
175     /* We can't really do anything useful here */
176     exit(1);
177   }
178   
179   usplash_progress(pipe_fd, 0);
180   md5_file = fopen(argv[2], "r");
181   if (!md5_file) {
182           perror("fopen md5_file");
183           exit(1);
184   }
185   while (fscanf(md5_file, "%as %as", &checksum, &checkfile) == 2) {
186     struct stat statbuf;
187
188     if (stat(checkfile, &statbuf) == 0) {
189       tsize += statbuf.st_size;
190     }
191
192     free(checksum);
193     free(checkfile);
194   }
195
196   rewind(md5_file);
197   while (fscanf(md5_file, "%as %as", &checksum, &checkfile) == 2) {
198     char buf[BUFSIZ];
199     ssize_t rsize;
200     int i;
201     
202     md5_init(&state);
203     
204     usplash_text(pipe_fd, "Checking %s", checkfile);
205     
206     check_fd = open(checkfile, O_RDONLY);
207     if (check_fd < 0) {
208       usplash_timeout(pipe_fd, 300);
209       usplash_failure(pipe_fd, "%s", strerror(errno));
210       sleep(10);
211     }
212     
213     rsize = read(check_fd, buf, sizeof(buf));
214
215     while (rsize > 0) {
216       csize += rsize;
217       usplash_progress(pipe_fd, floorl(100*csize/tsize));
218
219       md5_append(&state, (const md5_byte_t *)buf, rsize);
220       rsize = read(check_fd, buf, sizeof(buf));
221     }
222     
223     close(check_fd);
224     md5_finish(&state, digest);
225     
226     for (i = 0; i < 16; i++)
227       sprintf(hex_output + i * 2, "%02x", digest[i]);
228     
229     if (strncmp(hex_output, checksum, strlen(hex_output)) == 0) {
230       usplash_success(pipe_fd, "OK");
231     } else {
232       usplash_failure(pipe_fd, "mismatch");
233       failed++;
234     }
235     free(checksum);
236     free(checkfile);
237   }
238   usplash_text(pipe_fd, "Check finished, %d checksums failed", failed);
239   usplash_text(pipe_fd, "Press any key to reboot your system");
240   usplash_timeout(pipe_fd, 0);
241   set_nocanonical_tty(0);
242   getchar();
243   reboot(LINUX_REBOOT_CMD_RESTART);
244   return 0;
245   
246 }