1e9d1bb24cf9e554a9251ea0eab112ad65a3a8a8
[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_urgent(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, "TEXT-URGENT %s", s);
112
113   write_and_retry(fd, s1);
114
115   free(s);
116   free(s1);
117 }
118
119
120 void usplash_success(int fd, char *format, ...) {
121   char *s, *s1;
122   va_list argp;
123
124   va_start(argp, format);
125   vasprintf(&s, format, argp);
126   va_end(argp);
127
128   asprintf(&s1, "SUCCESS %s", s);
129
130   write_and_retry(fd, s1);
131   
132   free(s);
133   free(s1);
134 }
135
136 void usplash_progress(int fd, int progress) {
137   static int prevprogress = -1;
138   char *s;
139
140   if (progress == prevprogress)
141     return;
142   prevprogress = progress;
143
144   asprintf(&s, "PROGRESS %d", progress);
145
146   write_and_retry(fd, s);
147
148   free(s);
149 }
150
151 int set_nocanonical_tty(int fd) {
152   struct termios t;
153
154   if (tcgetattr(fd, &t) == -1) {
155     perror("tcgetattr");
156   }
157   t.c_lflag &= ~ICANON;
158   t.c_cc[VMIN] = 1;
159   t.c_cc[VTIME] = 0;
160   return tcsetattr(fd, TCSANOW, &t);
161 }
162
163 int main(int argc, char **argv) {
164   
165   int pipe_fd, check_fd;
166   int failed = 0;
167   
168   FILE *md5_file;
169   md5_state_t state;
170   md5_byte_t digest[16];
171   char hex_output[16*2 + 1];
172   char *checksum, *checkfile;
173   ssize_t tsize, csize;
174
175   tsize = 0;
176   csize = 0;
177
178   if (argc != 3) {
179     fprintf(stderr,"Wrong number of arguments\n");
180     fprintf(stderr,"%s <root directory> <md5sum file>\n", argv[0]);
181     exit(1);
182   }
183   
184   if (chdir(argv[1]) != 0) {
185     perror("chdir");
186     exit(1);
187   }
188   
189   pipe_fd = open(USPLASH_FIFO, O_WRONLY|O_NONBLOCK);
190   
191   if (pipe_fd == -1) {
192     /* We can't really do anything useful here */
193     perror("Opening pipe");
194     exit(1);
195   }
196   
197
198   usplash_progress(pipe_fd, 0);
199   usplash_urgent(pipe_fd, "Checking integrity, this may take some time");
200   md5_file = fopen(argv[2], "r");
201   if (!md5_file) {
202           perror("fopen md5_file");
203           exit(1);
204   }
205   while (fscanf(md5_file, "%as %as", &checksum, &checkfile) == 2) {
206     struct stat statbuf;
207
208     if (stat(checkfile, &statbuf) == 0) {
209       tsize += statbuf.st_size;
210     }
211
212     free(checksum);
213     free(checkfile);
214   }
215
216   rewind(md5_file);
217   while (fscanf(md5_file, "%as %as", &checksum, &checkfile) == 2) {
218     char buf[BUFSIZ];
219     ssize_t rsize;
220     int i;
221     
222     md5_init(&state);
223     
224     usplash_text(pipe_fd, "Checking %s", checkfile);
225     
226     check_fd = open(checkfile, O_RDONLY);
227     if (check_fd < 0) {
228       usplash_timeout(pipe_fd, 300);
229       usplash_failure(pipe_fd, "%s", strerror(errno));
230       sleep(10);
231     }
232     
233     rsize = read(check_fd, buf, sizeof(buf));
234
235     while (rsize > 0) {
236       csize += rsize;
237       usplash_progress(pipe_fd, floorl(100*((long double)csize)/tsize));
238
239       md5_append(&state, (const md5_byte_t *)buf, rsize);
240       rsize = read(check_fd, buf, sizeof(buf));
241     }
242     
243     close(check_fd);
244     md5_finish(&state, digest);
245     
246     for (i = 0; i < 16; i++)
247       sprintf(hex_output + i * 2, "%02x", digest[i]);
248     
249     if (strncmp(hex_output, checksum, strlen(hex_output)) == 0) {
250       usplash_success(pipe_fd, "OK");
251     } else {
252       usplash_failure(pipe_fd, "mismatch");
253       failed++;
254     }
255     free(checksum);
256     free(checkfile);
257   }
258   usplash_urgent(pipe_fd, "Check finished, %d checksums failed", failed);
259   usplash_urgent(pipe_fd, "Press any key to reboot your system");
260   usplash_timeout(pipe_fd, 86400);
261   set_nocanonical_tty(0);
262   getchar();
263   reboot(LINUX_REBOOT_CMD_RESTART);
264   return 0;
265   
266 }