Merge from vendor branch DIFFUTILS:
[dragonfly.git] / tools / 3.0-upgrade / cvt-wtmp.c
1 /*
2  * Copyright (c) 1996 Joerg Wunsch
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD: src/tools/3.0-upgrade/cvt-wtmp.c,v 1.6 1999/08/28 00:54:20 peter Exp $
27  * $DragonFly: src/tools/3.0-upgrade/Attic/cvt-wtmp.c,v 1.2 2003/06/17 04:29:10 dillon Exp $
28  *
29  */
30
31 /*
32  * Heuristics to convert old wtmp format to new one.
33  */
34
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38
39 #include <err.h>
40 #include <fcntl.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sysexits.h>
45 #include <unistd.h>
46 #include <utmp.h>
47
48
49 #define OUT_NAMESIZE    8
50 #define OUT_LINESIZE    8
51 #define OUT_HOSTSIZE    16
52
53 struct olastlog {
54         time_t  ll_time;
55         char    ll_line[OUT_LINESIZE];
56         char    ll_host[OUT_HOSTSIZE];
57 };
58
59 struct outmp {
60         char    ut_line[OUT_LINESIZE];
61         char    ut_name[OUT_NAMESIZE];
62         char    ut_host[OUT_HOSTSIZE];
63         long    ut_time;
64 };
65
66 void    usage(void);
67 void    convert(const char *, int, int);
68
69 /*
70  * NB: We cannot convert lastlog yet, but we don't need either.
71  */
72
73 void
74 usage(void)
75 {
76   errx(EX_USAGE, "usage: cvt-wtmp [-f] [-n] /var/log/wtmp*");
77 }
78
79
80 int
81 main(int argc, char **argv)
82 {
83   int errs, i, nflag, forceflag, rv;
84
85   errs = nflag = forceflag = 0;
86   while ((i = getopt(argc, argv, "fn")) != -1)
87     switch (i)
88       {
89       case 'f':
90         forceflag++;
91         break;
92
93       case 'n':
94         nflag++;
95         break;
96
97       default:
98         errs++;
99       }
100   argc -= optind;
101   argv += optind;
102
103   if (argc <= 0 || errs)
104     usage();
105
106   for (;argc > 0; argc--, argv++)
107     convert(*argv, nflag, forceflag);
108
109   return 0;
110 }
111
112 void
113 convert(const char *name, int nflag, int forceflag)
114 {
115   struct stat sb;
116   struct timeval tv[2];
117   char xname[1024], yname[1024];
118   unsigned char buf[128];       /* large enough to hold one wtmp record */
119   int fd1, fd2;
120   size_t off, shouldbe;
121   int old, new;
122   time_t now, early, *t;
123   struct tm tm;
124   struct utmp u;
125   struct outmp *ou;
126   enum { OLD, NEW } which = OLD; /* what we're defaulting to */
127
128   if (stat(name, &sb) == -1)
129     {
130       warn("Cannot stat file \"%s\", continuing.", name);
131       return;
132     }
133
134   now = time(NULL);
135   /* some point in time very early, before 386BSD 0.0 */
136   tm.tm_sec = 0; tm.tm_min = 0; tm.tm_hour = 0;
137   tm.tm_mday = 1; tm.tm_mon = 2; tm.tm_year = 92;
138   tm.tm_isdst = 0;
139   early = mktime(&tm);
140
141   tv[0].tv_sec = sb.st_atimespec.tv_sec;
142   tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000;
143   tv[1].tv_sec = sb.st_mtimespec.tv_sec;
144   tv[1].tv_usec = sb.st_mtimespec.tv_nsec / 1000;
145
146   /* unzipping is handled best externally */
147   if (strlen(name) > 3 && memcmp(&name[strlen(name) - 3], ".gz", 3) == 0)
148     {
149       warnx("Cannot handle gzipped files, ignoring \"%s\".", name);
150       return;
151     }
152
153   (void) snprintf(xname, sizeof xname, "%s.new", name);
154   if (!nflag && (fd1 = open(xname, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
155     err(EX_CANTCREAT, "Can't create new wtmp file");
156
157   if ((fd2 = open(name, O_RDONLY, 0)) == -1)
158     err(EX_UNAVAILABLE, "input file magically disappeared, i'm confused");
159
160   old = new = 0; off = 0;
161   memset(buf, 0, sizeof buf);
162
163   while (read(fd2, &buf[off], sizeof(time_t)) == sizeof(time_t))
164     {
165       t = (time_t *)&buf[off];
166       off += sizeof(time_t);
167       if (off < sizeof(struct outmp))
168         /* go on */
169         continue;
170       if (*t < early || *t > now)
171         {
172           /* unreasonable, collect another entry */
173           if (off > sizeof buf)
174             {
175               if (!forceflag)
176                 {
177                   (void) unlink(xname);
178                   errx(EX_UNAVAILABLE, "I can't seem to make sense out of file \"%s\",\n"
179                        "Could have forced using -f.",
180                      name);
181                 }
182               else
183                 {
184                   warnx("Record # %d in file \"%s\" seems bogus\n"
185                         "(time: %d, previous time: %d, now: %d),\n"
186                         "continuing anyway.",
187                         old + new + 1, name, *t, early, now);
188                   if (which == NEW)
189                     {
190                       (void)lseek(fd2, sizeof(struct utmp) - sizeof buf, SEEK_CUR);
191                       goto write_new;
192                     }
193                   else
194                     {
195                       (void)lseek(fd2, sizeof(struct outmp) - sizeof buf, SEEK_CUR);
196                       goto write_old;
197                     }
198                 }
199             }
200           continue;
201         }
202       /* time is reasonable, we seem to have collected a full entry */
203       if (off == sizeof(struct utmp))
204         {
205           /* new wtmp record */
206           which = NEW;
207         write_new:
208           new++;
209           if (!nflag)
210             {
211               if (write(fd1, buf, sizeof(struct utmp)) != sizeof(struct utmp))
212                 err(EX_IOERR, "writing file \"%s\"", xname);
213             }
214         }
215       else if (off == sizeof(struct outmp))
216         {
217           /* old fart */
218           which = OLD;
219         write_old:
220           old++;
221           if (!nflag)
222             {
223               ou = (struct outmp *)buf;
224               memset(&u, 0, sizeof u);
225               memcpy(&u.ut_line, ou->ut_line, OUT_LINESIZE);
226               memcpy(&u.ut_name, ou->ut_name, OUT_NAMESIZE);
227               memcpy(&u.ut_host, ou->ut_host, OUT_HOSTSIZE);
228               memcpy(&u.ut_time, &ou->ut_time, sizeof u.ut_time);
229               if (write(fd1, &u, sizeof(struct utmp)) != sizeof(struct utmp))
230                 err(EX_IOERR, "writing file \"%s\"", xname);
231             }
232         }
233       else
234         {
235           if (!forceflag)
236             {
237               warnx("Illegal record in file \"%s\", ignoring.", name);
238               off = 0;
239               continue;
240             }
241           else
242             {
243               warnx("Illegal record in file \"%s\", considering it %s one.",
244                     name, (which == OLD? "an old": "a new"));
245               shouldbe = (which == OLD? sizeof(struct outmp): sizeof(struct utmp));
246               if (off < shouldbe)
247                 (void)read(fd2, &buf[off], shouldbe - off);
248               else
249                 (void)lseek(fd2, shouldbe - off, SEEK_CUR);
250               if (which == OLD)
251                 goto write_old;
252               else
253                 goto write_new;
254             }
255         }
256       off = 0;
257       /*
258        * Since the wtmp file is in chronologically acsending order, we
259        * can move the `early' time as we go.  Allow for one hour
260        * time-of-day adjustments.
261        */
262       early = *t - 3600;
263       memset(buf, 0, sizeof buf);
264     }
265   close(fd2);
266
267   printf("File \"%s\": %d old and %d new records found.\n",
268          name, old, new);
269
270   if (nflag)
271     return;
272
273   (void) close(fd1);
274   (void) snprintf(yname, sizeof yname, "%s.bak", name);
275
276   if (rename(name, yname) == -1)
277     err(EX_OSERR, "Cannot rename \"%s\" to \"%s\"", name, yname);
278   
279   if (rename(xname, name) == -1)
280     err(EX_OSERR, "Cannot rename \"%s\" to \"%s\"", xname, name);
281
282   if (utimes(name, tv) == -1)
283     warn("Cannot adjust access and modification times for \"%s\"", name);
284 }
285