]> git.wh0rd.org Git - dump.git/blob - common/dumprmt.c
941f7dd74163b2fad6d670dc3c2a6815bad8d1d8
[dump.git] / common / dumprmt.c
1 /*
2  *      Ported to Linux's Second Extended File System as part of the
3  *      dump and restore backup suit
4  *      Remy Card <card@Linux.EU.Org>, 1994-1997
5  *      Stelian Pop <pop@noos.fr>, 1999-2000
6  *      Stelian Pop <pop@noos.fr> - AlcĂ´ve <www.alcove.fr>, 2000
7  */
8
9 /*-
10  * Copyright (c) 1980, 1993
11  *      The Regents of the University of California.  All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *      This product includes software developed by the University of
24  *      California, Berkeley and its contributors.
25  * 4. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  */
41
42 #ifndef lint
43 static const char rcsid[] =
44         "$Id: dumprmt.c,v 1.18 2001/08/13 16:17:52 stelian Exp $";
45 #endif /* not lint */
46
47 #include <config.h>
48 #include <sys/param.h>
49 #include <sys/mtio.h>
50 #include <sys/socket.h>
51 #include <sys/time.h>
52 #ifdef __linux__
53 #include <sys/types.h>
54 #ifdef HAVE_EXT2FS_EXT2_FS_H
55 #include <ext2fs/ext2_fs.h>
56 #else
57 #include <linux/ext2_fs.h>
58 #endif
59 #include <bsdcompat.h>
60 #include <signal.h>
61 #elif defined sunos
62 #include <sys/vnode.h>
63
64 #include <ufs/inode.h>
65 #else
66 #include <ufs/ufs/dinode.h>
67 #endif
68
69 #include <netinet/in.h>
70 #include <netinet/in_systm.h>
71 #include <netinet/ip.h>
72 #include <netinet/tcp.h>
73
74 #include <protocols/dumprestore.h>
75
76 #include <ctype.h>
77 #include <errno.h>
78 #include <compaterr.h>
79 #include <netdb.h>
80 #include <pwd.h>
81 #include <stdio.h>
82 #ifdef __STDC__
83 #include <stdlib.h>
84 #include <string.h>
85 #include <unistd.h>
86 #endif
87
88 #ifdef  __linux__
89 #include <ext2fs/ext2fs.h>
90 #endif
91
92 #include <pathnames.h>
93 #include "dump.h"       /* for X_STARTUP, X_ABORT etc */
94
95 #define TS_CLOSED       0
96 #define TS_OPEN         1
97
98 static  int rmtstate = TS_CLOSED;
99 static  int tormtape = -1;
100 static  int fromrmtape = -1;
101 int rshpid = -1;
102 static  const char *rmtpeer = 0;
103
104 static  int okname __P((const char *));
105 static  int rmtcall __P((const char *, const char *));
106 static  void rmtconnaborted __P((int));
107 static  int rmtgetb __P((void));
108 static  int rmtgetconn __P((void));
109 static  void rmtgets __P((char *, size_t));
110 static  int rmtreply __P((const char *));
111 static  int piped_child __P((const char **command));
112 #ifdef KERBEROS
113 int     krcmd __P((char **, int /*u_short*/, char *, char *, int *, char *));
114 #endif
115
116 static  int errfd = -1;
117 extern  int dokerberos;
118 extern  int ntrec;              /* blocking factor on tape */
119 #ifndef errno
120 extern  int errno;
121 #endif
122
123 int
124 rmthost(const char *host)
125 {
126         if (rmtpeer)
127                 free((void *)rmtpeer);
128         if ((rmtpeer = strdup(host)) == NULL)
129                 rmtpeer = host;
130         signal(SIGPIPE, rmtconnaborted);
131         return rmtgetconn();
132 }
133
134 static void
135 rmtconnaborted(int signo)
136 {
137         msg("Lost connection to remote host.\n");
138         if (errfd != -1) {
139                 fd_set r;
140                 struct timeval t;
141
142                 FD_ZERO(&r);
143                 FD_SET(errfd, &r);
144                 t.tv_sec = 0;
145                 t.tv_usec = 0;
146                 if (select(errfd + 1, &r, NULL, NULL, &t)) {
147                         int i;
148                         char buf[2048];
149
150                         if ((i = read(errfd, buf, sizeof(buf) - 1)) > 0) {
151                                 buf[i] = '\0';
152                                 msg("on %s: %s%s", rmtpeer, buf,
153                                         buf[i - 1] == '\n' ? "" : "\n");
154                         }
155                 }
156         }
157
158         exit(X_ABORT);
159 }
160
161 static int
162 rmtgetconn(void)
163 {
164         register char *cp;
165         register const char *rmt;
166         static struct servent *sp = NULL;
167         static struct passwd *pwd = NULL;
168         const char *tuser;
169         const char *rsh;
170         int size;
171         int throughput;
172         int on;
173         char *rmtpeercopy;
174
175         rsh = getenv("RSH");
176
177         if (!rsh && sp == NULL) {
178                 sp = getservbyname(dokerberos ? "kshell" : "shell", "tcp");
179                 if (sp == NULL)
180                         errx(1, "%s/tcp: unknown service",
181                             dokerberos ? "kshell" : "shell");
182         }
183         if (pwd == NULL) {
184                 pwd = getpwuid(getuid());
185                 if (pwd == NULL)
186                         errx(1, "who are you?");
187         }
188         if ((cp = strchr(rmtpeer, '@')) != NULL) {
189                 tuser = rmtpeer;
190                 *cp = '\0';
191                 if (!okname(tuser))
192                         exit(X_STARTUP);
193                 rmtpeer = ++cp;
194         } else
195                 tuser = pwd->pw_name;
196         if ((rmt = getenv("RMT")) == NULL)
197                 rmt = _PATH_RMT;
198         msg("");
199
200         if (rsh) {
201                 const char *rshcmd[6];
202                 rshcmd[0] = rsh;
203                 rshcmd[1] = rmtpeer;
204                 rshcmd[2] = "-l";
205                 rshcmd[3] = tuser;
206                 rshcmd[4] = rmt;
207                 rshcmd[5] = NULL;
208
209                 /* Restore the uid and gid. We really don't want
210                  * to execute whatever is put into RSH variable with
211                  * more priviledges than needed... */
212                 setuid(getuid());
213                 setgid(getgid());
214
215                 if ((rshpid = piped_child(rshcmd)) < 0) {
216                         msg("cannot open connection\n");
217                         return 0;
218                 }
219         }
220         else {
221                 /* Copy rmtpeer to rmtpeercopy to ignore the
222                    return value from rcmd. I cannot figure if
223                    this is this a bug in rcmd or in my code... */
224                 rmtpeercopy = (char *)rmtpeer;
225 #ifdef KERBEROS
226                 if (dokerberos)
227                         tormtape = krcmd(&rmtpeercopy, sp->s_port, tuser, rmt, &errfd,
228                                        (char *)0);
229                 else
230 #endif
231                         tormtape = rcmd(&rmtpeercopy, (u_short)sp->s_port, pwd->pw_name,
232                                       tuser, rmt, &errfd);
233                 if (tormtape < 0) {
234                         msg("login to %s as %s failed.\n", rmtpeer, tuser);
235                         return 0;
236                 }
237                 size = ntrec * TP_BSIZE;
238                 if (size > 60 * 1024)           /* XXX */
239                         size = 60 * 1024;
240                 /* Leave some space for rmt request/response protocol */
241                 size += 2 * 1024;
242                 while (size > TP_BSIZE &&
243                     setsockopt(tormtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0)
244                             size -= TP_BSIZE;
245                 (void)setsockopt(tormtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size));
246                 throughput = IPTOS_THROUGHPUT;
247                 if (setsockopt(tormtape, IPPROTO_IP, IP_TOS,
248                     &throughput, sizeof(throughput)) < 0)
249                         perror("IP_TOS:IPTOS_THROUGHPUT setsockopt");
250                 on = 1;
251                 if (setsockopt(tormtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
252                         perror("TCP_NODELAY setsockopt");
253                 fromrmtape = tormtape;
254         }
255         (void)fprintf(stderr, "Connection to %s established.\n", rmtpeer);
256         return 1;
257 }
258
259 static int
260 okname(const char *cp0)
261 {
262         register const char *cp;
263         register int c;
264
265         for (cp = cp0; *cp; cp++) {
266                 c = *cp;
267                 if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) {
268                         warnx("invalid user name %s\n", cp0);
269                         return (0);
270                 }
271         }
272         return (1);
273 }
274
275 int
276 rmtopen(const char *tape, int mode)
277 {
278         char buf[MAXPATHLEN];
279
280         (void)snprintf(buf, sizeof (buf), "O%s\n%d\n", tape, mode);
281         rmtstate = TS_OPEN;
282         return (rmtcall(tape, buf));
283 }
284
285 void
286 rmtclose(void)
287 {
288
289         if (rmtstate != TS_OPEN)
290                 return;
291         rmtcall("close", "C\n");
292         rmtstate = TS_CLOSED;
293 }
294
295 int
296 rmtread(char *buf, size_t count)
297 {
298         char line[30];
299         int n, i;
300         ssize_t cc;
301
302         (void)snprintf(line, sizeof (line), "R%u\n", (unsigned)count);
303         n = rmtcall("read", line);
304         if (n < 0)
305                 /* rmtcall() properly sets errno for us on errors. */
306                 return (n);
307         for (i = 0; i < n; i += cc) {
308                 cc = read(fromrmtape, buf+i, n - i);
309                 if (cc <= 0)
310                         rmtconnaborted(0);
311         }
312         return (n);
313 }
314
315 int
316 rmtwrite(const char *buf, size_t count)
317 {
318         char line[30];
319
320         (void)snprintf(line, sizeof (line), "W%ld\n", (long)count);
321         write(tormtape, line, strlen(line));
322         write(tormtape, buf, count);
323         return (rmtreply("write"));
324 }
325
326 int
327 rmtseek(int offset, int pos)
328 {
329         char line[80];
330
331         (void)snprintf(line, sizeof (line), "L%d\n%d\n", offset, pos);
332         return (rmtcall("seek", line));
333 }
334
335 struct  mtget mts;
336
337 struct mtget *
338 rmtstatus(void)
339 {
340         register int i;
341         register char *cp;
342
343         if (rmtstate != TS_OPEN)
344                 return (NULL);
345         rmtcall("status", "S\n");
346         for (i = 0, cp = (char *)&mts; i < sizeof(mts); i++)
347                 *cp++ = rmtgetb();
348         return (&mts);
349 }
350
351 int
352 rmtioctl(int cmd, int count)
353 {
354         char buf[256];
355
356         if (count < 0)
357                 return (-1);
358         (void)snprintf(buf, sizeof (buf), "I%d\n%d\n", cmd, count);
359         return (rmtcall("ioctl", buf));
360 }
361
362 static int
363 rmtcall(const char *cmd, const char *buf)
364 {
365
366         if (write(tormtape, buf, strlen(buf)) != strlen(buf))
367                 rmtconnaborted(0);
368         return (rmtreply(cmd));
369 }
370
371 static int
372 rmtreply(const char *cmd)
373 {
374         register char *cp;
375         char code[30], emsg[BUFSIZ];
376
377         rmtgets(code, sizeof (code));
378         if (*code == 'E' || *code == 'F') {
379                 rmtgets(emsg, sizeof (emsg));
380                 msg("%s: %s", cmd, emsg);
381                 errno = atoi(code + 1);
382                 if (*code == 'F')
383                         rmtstate = TS_CLOSED;
384                 return (-1);
385         }
386         if (*code != 'A') {
387                 /* Kill trailing newline */
388                 cp = code + strlen(code);
389                 if (cp > code && *--cp == '\n')
390                         *cp = '\0';
391
392                 msg("Protocol to remote tape server botched (code \"%s\").\n",
393                     code);
394                 rmtconnaborted(0);
395         }
396         return (atoi(code + 1));
397 }
398
399 static int
400 rmtgetb(void)
401 {
402         char c;
403
404         if (read(fromrmtape, &c, 1) != 1)
405                 rmtconnaborted(0);
406         return (c);
407 }
408
409 /* Get a line (guaranteed to have a trailing newline). */
410 static void
411 rmtgets(char *line, size_t len)
412 {
413         register char *cp = line;
414
415         while (len > 1) {
416                 *cp = rmtgetb();
417                 if (*cp == '\n') {
418                         cp[1] = '\0';
419                         return;
420                 }
421                 cp++;
422                 len--;
423         }
424         *cp = '\0';
425         msg("Protocol to remote tape server botched.\n");
426         msg("(rmtgets got \"%s\").\n", line);
427         rmtconnaborted(0);
428 }
429
430 int piped_child(const char **command) {
431         int pid;
432         int to_child_pipe[2];
433         int from_child_pipe[2];
434
435         if (pipe (to_child_pipe) < 0) {
436                 msg ("cannot create pipe: %s\n", strerror(errno));
437                 return -1;
438         }
439         if (pipe (from_child_pipe) < 0) {
440                 msg ("cannot create pipe: %s\n", strerror(errno)); 
441                 return -1;
442         }
443         pid = fork ();
444         if (pid < 0) {
445                 msg ("cannot fork: %s\n", strerror(errno));
446                 return -1;
447         }
448         if (pid == 0) {
449                 if (dup2 (to_child_pipe[0], STDIN_FILENO) < 0) {
450                         msg ("cannot dup2 pipe: %s\n", strerror(errno));
451                         exit(1);
452                 }
453                 if (close (to_child_pipe[1]) < 0) {
454                         msg ("cannot close pipe: %s\n", strerror(errno));
455                         exit(1);
456                 }
457                 if (close (from_child_pipe[0]) < 0) {
458                         msg ("cannot close pipe: %s\n", strerror(errno));
459                         exit(1);
460                 }
461                 if (dup2 (from_child_pipe[1], STDOUT_FILENO) < 0) {
462                         msg ("cannot dup2 pipe: %s\n", strerror(errno));
463                         exit(1);
464                 }
465                 setpgid(0, getpid());
466                 execvp (command[0], (char *const *) command);
467                 msg("cannot exec %s: %s\n", command[0], strerror(errno));
468                 exit(1);
469         }
470         if (close (to_child_pipe[0]) < 0) {
471                 msg ("cannot close pipe: %s\n", strerror(errno));
472                 return -1;
473         }
474         if (close (from_child_pipe[1]) < 0) {
475                 msg ("cannot close pipe: %s\n", strerror(errno));
476                 return -1;
477         }
478         tormtape = to_child_pipe[1];
479         fromrmtape = from_child_pipe[0];
480         return pid;
481 }