openafs-string-header-cleanup-20071030
[openafs.git] / src / update / server.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID
14     ("$Header$");
15
16 #include <afs/stds.h>
17 #ifdef  AFS_AIX32_ENV
18 #include <signal.h>
19 #endif
20 #include <sys/types.h>
21 #ifdef AFS_NT40_ENV
22 #include <winsock2.h>
23 #include <WINNT/afsevent.h>
24 #include <fcntl.h>
25 #include <io.h>
26 #include <afs/dirent.h>
27 #include <afs/procmgmt.h>
28 #else
29 #include <netdb.h>
30 #include <netinet/in.h>
31 #include <sys/file.h>
32 #include <dirent.h>
33 #endif
34 #include <string.h>
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #include <sys/stat.h>
39 #include <errno.h>
40 #include <stdio.h>
41 #include <rx/xdr.h>
42 #include <rx/rx.h>
43 #include <rx/rxkad.h>
44 #include <afs/cellconfig.h>
45 #include <afs/afsutil.h>
46 #include <afs/fileutil.h>
47 #include <afs/com_err.h>
48 #ifdef  AFS_AIX_ENV
49 #include <sys/statfs.h>
50 #endif
51 #include "update.h"
52 #include "global.h"
53
54 extern int UPDATE_ExecuteRequest();
55
56 static int AddObject(char **expPath, char *dir);
57 static int PathInDirectory(char *dir, char *path);
58
59 struct afsconf_dir *cdir;
60 int nDirs;
61 char *dirName[MAXENTRIES];
62 int dirLevel[MAXENTRIES];
63 char *whoami;
64
65 static int Quit();
66
67 int rxBind = 0;
68
69 #define ADDRSPERSITE 16         /* Same global is in rx/rx_user.c */
70 afs_uint32 SHostAddrs[ADDRSPERSITE];
71
72 /* check whether caller is authorized to manage RX statistics */
73 int
74 update_rxstat_userok(struct rx_call *call)
75 {
76     return afsconf_SuperUser(cdir, call, NULL);
77 }
78
79 /*
80  * PathInDirectory() -- determine if path is in directory (or is directory)
81  */
82 static int
83 PathInDirectory(char *dir, char *path)
84 {
85     int inDir = 0;
86     size_t dirLen;
87     char dirNorm[AFSDIR_PATH_MAX], pathNorm[AFSDIR_PATH_MAX];
88
89 #ifdef AFS_NT40_ENV
90     /* case-insensitive comparison of normalized, same-flavor (short) paths */
91     DWORD status;
92
93     status = GetShortPathName(dir, dirNorm, AFSDIR_PATH_MAX);
94     if (status == 0 || status > AFSDIR_PATH_MAX) {
95         /* can't convert path to short version; just use long version */
96         strcpy(dirNorm, dir);
97     }
98     FilepathNormalize(dirNorm);
99
100     status = GetShortPathName(path, pathNorm, AFSDIR_PATH_MAX);
101     if (status == 0 || status > AFSDIR_PATH_MAX) {
102         /* can't convert path to short version; just use long version */
103         strcpy(pathNorm, path);
104     }
105     FilepathNormalize(pathNorm);
106
107     dirLen = strlen(dirNorm);
108
109     if (_strnicmp(dirNorm, pathNorm, dirLen) == 0) {
110         /* substrings match; path must match dir or be subdirectory */
111         if (pathNorm[dirLen] == '\0' || pathNorm[dirLen] == '/') {
112             inDir = 1;
113         }
114     }
115 #else
116     /* case-sensitive comparison of normalized paths */
117     strcpy(dirNorm, dir);
118     FilepathNormalize(dirNorm);
119
120     strcpy(pathNorm, path);
121     FilepathNormalize(pathNorm);
122
123     dirLen = strlen(dirNorm);
124
125     if (strncmp(dirNorm, pathNorm, dirLen) == 0) {
126         /* substrings match; path must match dir or be subdirectory */
127         if (pathNorm[dirLen] == '\0' || pathNorm[dirLen] == '/') {
128             inDir = 1;
129         }
130     }
131 #endif /* AFS_NT40_ENV */
132     return inDir;
133 }
134
135 int
136 AuthOkay(struct rx_call *call, char *name)
137 {
138     int i;
139     rxkad_level level;
140     afs_int32 code;
141     int matches;
142
143     /* Must be in 'UserList' to use */
144     if (!afsconf_SuperUser(cdir, call, NULL))
145         return 0;
146
147     if (rx_SecurityClassOf(rx_ConnectionOf(call)) == 2) {
148         code = rxkad_GetServerInfo(call->conn, &level, 0, 0, 0, 0, 0);
149         if (code)
150             return 0;
151     } else
152         level = 0;
153
154     matches = 0;
155     for (i = 0; i < nDirs; i++) {
156         if (PathInDirectory(dirName[i], name)) {
157             if (dirLevel[i] > level)
158                 return 0;
159             matches++;
160             /* keep searching in case there's a more restrictive subtree
161              * specified later. */
162         }
163     }
164     if (nDirs && !matches)
165         return 0;               /* if dirs spec., name must match */
166     return 1;                   /* okay or no dirs */
167 }
168
169 int
170 osi_audit()
171 {
172 /* this sucks but it works for now.
173 */
174     return 0;
175 }
176
177 #ifndef AFS_NT40_ENV
178 #include "AFS_component_version_number.c"
179 #endif
180
181 int
182 main(int argc, char *argv[])
183 {
184     struct rx_securityClass *securityObjects[3];
185     struct rx_service *service;
186     afs_uint32 host = htonl(INADDR_ANY);
187
188     int a = 0;
189     rxkad_level level;
190     rxkad_level newLevel;
191
192 #ifdef  AFS_AIX32_ENV
193     /*
194      * The following signal action for AIX is necessary so that in case of a 
195      * crash (i.e. core is generated) we can include the user's data section 
196      * in the core dump. Unfortunately, by default, only a partial core is
197      * generated which, in many cases, isn't too useful.
198      */
199     struct sigaction nsa;
200
201     sigemptyset(&nsa.sa_mask);
202     nsa.sa_handler = SIG_DFL;
203     nsa.sa_flags = SA_FULLDUMP;
204     sigaction(SIGABRT, &nsa, NULL);
205     sigaction(SIGSEGV, &nsa, NULL);
206 #endif
207
208     whoami = argv[0];
209
210 #ifdef AFS_NT40_ENV
211     /* dummy signal call to force afsprocmgmt.dll to load on NT */
212     signal(SIGUSR1, SIG_DFL);
213 #endif
214
215     /* Initialize dirpaths */
216     if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
217 #ifdef AFS_NT40_ENV
218         ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
219 #endif
220         fprintf(stderr, "%s: Unable to obtain AFS server directory.\n",
221                 argv[0]);
222         exit(2);
223     }
224     nDirs = 0;
225     level = rxkad_clear;
226
227     if (argc == 1)              /* no arguments */
228         goto usage;
229
230     /* support help flag */
231     if (strcmp("-help", argv[1]) == 0)
232         goto usage;
233     if (strcmp("help", argv[1]) == 0)
234         goto usage;
235
236     for (a = 1; a < argc; a++) {
237         if (argv[a][0] == '-') {        /* parse options */
238             if (strcmp(argv[a], "-rxbind") == 0) {
239                 rxBind = 1;
240                 continue;
241             } else {
242                 char arg[256];
243                 lcstring(arg, argv[a], sizeof(arg));
244                 newLevel = rxkad_StringToLevel(&argv[a][1]);
245                 if (newLevel != -1) {
246                     level = newLevel;   /* set new level */
247                     continue;
248                 }
249             }
250           usage:
251             Quit("Usage: upserver [<directory>+] [-crypt <directory>+] [-clear <directory>+] [-auth <directory>+] [-rxbind] [-help]\n");
252         } else {
253             int dirlen;
254             if (nDirs >= sizeof(dirName) / sizeof(dirName[0]))
255                 Quit("Too many dirs");
256             dirlen = strlen(argv[a]);
257             if (AddObject(&dirName[nDirs], argv[a])) {
258                 printf("%s: Unable to export dir %s. Skipping\n", whoami,
259                        argv[a]);
260                 continue;
261             }
262             dirLevel[nDirs] = level;    /* remember current level */
263             nDirs++;
264         }
265     }
266
267     if (nDirs == 0) {           /* Didn't find any directories to export */
268         printf("%s: No directories to export. Quitting\n", whoami);
269         exit(1);
270     }
271
272     cdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
273     if (cdir == 0) {
274         fprintf(stderr, "Can't get server configuration info (%s)\n",
275                 AFSDIR_SERVER_ETC_DIRPATH);
276         exit(1);
277     }
278
279     if (rxBind) {
280         afs_int32 ccode;
281 #ifndef AFS_NT40_ENV
282         if (AFSDIR_SERVER_NETRESTRICT_FILEPATH || 
283             AFSDIR_SERVER_NETINFO_FILEPATH) {
284             char reason[1024];
285             ccode = parseNetFiles(SHostAddrs, NULL, NULL,
286                                            ADDRSPERSITE, reason,
287                                            AFSDIR_SERVER_NETINFO_FILEPATH,
288                                            AFSDIR_SERVER_NETRESTRICT_FILEPATH);
289         } else 
290 #endif  
291         {
292             ccode = rx_getAllAddr(SHostAddrs, ADDRSPERSITE);
293         }
294         if (ccode == 1) 
295             host = SHostAddrs[0];
296     }
297
298     /* Initialize Rx, telling it port number this server will use for its
299      * single service */
300     if (rx_InitHost(host, htons(AFSCONF_UPDATEPORT)) < 0)
301         Quit("rx_init");
302
303     /* Create a single security object, in this case the null security object,
304      * for unauthenticated connections, which will be used to control security
305      * on connections made to this server. */
306
307     /* WHAT A JOKE!  Let's use rxkad at least so we know who we're talking to,
308      * then sometimes require full encryption. */
309
310     /* rxnull and rxvab are no longer supported */
311     securityObjects[0] = rxnull_NewServerSecurityObject();
312
313     securityObjects[1] = (struct rx_securityClass *)0;
314
315     securityObjects[2] =
316         rxkad_NewServerSecurityObject(rxkad_clear, cdir, afsconf_GetKey, 0);
317     if (securityObjects[2] == (struct rx_securityClass *)0)
318         Quit("rxkad_NewServerSecurityObject");
319
320     /* Instantiate a single UPDATE service.  The rxgen-generated procedure
321      * which is called to decode requests is passed in here
322      * (UPDATE_ExecuteRequest). */
323     service =
324         rx_NewServiceHost(host, 0, UPDATE_SERVICEID, "UPDATE", securityObjects,
325                           3, UPDATE_ExecuteRequest);
326     if (service == (struct rx_service *)0)
327         Quit("rx_NewService");
328     rx_SetMaxProcs(service, 2);
329
330     /* allow super users to manage RX statistics */
331     rx_SetRxStatUserOk(update_rxstat_userok);
332
333     rx_StartServer(1);          /* Donate this process to the server process pool */
334     Quit("StartServer returned?");
335     return 0;
336 }
337
338 /* fetch the file name and send it to the remote requester specified by call */
339
340 int
341 UPDATE_FetchFile(struct rx_call *call, char *name)
342 {
343     int fd = -1;
344     int error = 0;
345     struct stat status;
346     char *reqObject;
347
348     /* construct a local path from a canonical (wire-format) path */
349     if ((error = ConstructLocalPath(name, "/", &reqObject))) {
350         afs_com_err(whoami, error, "Unable to construct local path");
351         return UPDATE_ERROR;
352     }
353
354     if (!AuthOkay(call, reqObject)) {
355         error = UPDATE_ERROR;
356     } else {
357         fd = open(reqObject, O_RDONLY, 0);
358         if (fd < 0 || fstat(fd, &status) < 0) {
359             printf("Failed to open %s\n", reqObject);
360             error = UPDATE_ERROR;
361         }
362         if (!error)
363             error = update_SendFile(fd, call, &status);
364         if (fd >= 0)
365             close(fd);
366     }
367     free(reqObject);
368     return error;
369 }
370
371 /* fetch dir info about directory name and send it to remote host associated
372   with call. */
373 int
374 UPDATE_FetchInfo(struct rx_call *call, char *name)
375 {
376     int error = 0;
377     struct stat status;
378     char *reqObject;
379
380     /* construct a local path from a canonical (wire-format) path */
381     if ((error = ConstructLocalPath(name, "/", &reqObject))) {
382         afs_com_err(whoami, error, "Unable to construct local path");
383         return UPDATE_ERROR;
384     }
385
386     if (!AuthOkay(call, reqObject)) {
387         error = UPDATE_ERROR;
388     } else {
389         /* we only need to stat the obj, not open it. */
390         if (stat(reqObject, &status) < 0) {
391             printf("Failed to open %s\n", reqObject);
392             error = UPDATE_ERROR;
393         }
394         if ((status.st_mode & S_IFMT) != S_IFDIR) {
395             printf(" file %s is not a directory \n", reqObject);
396             error = -1;
397         }
398
399         if (!error)
400             error = update_SendDirInfo(reqObject, call, &status, name);
401     }
402     free(reqObject);
403     return error;
404 }
405
406 static int
407 Quit(msg, a, b)
408      char *msg;
409 {
410     fprintf(stderr, msg, a, b);
411     exit(1);
412 }
413
414 int
415 update_SendFile(register int fd, register struct rx_call *call, register struct stat *status)
416 {
417     char *buffer = (char *)0;
418     int blockSize;
419     afs_int32 length, tlen;
420 #ifdef  AFS_AIX_ENV
421     struct statfs tstatfs;
422 #endif
423
424     afs_int32 error = 0;
425 #ifdef  AFS_AIX_ENV
426     /* Unfortunately in AIX valuable fields such as st_blksize are gone from the stat structure!! */
427     fstatfs(fd, &tstatfs);
428     blockSize = tstatfs.f_bsize;
429 #elif AFS_NT40_ENV
430     blockSize = 4096;
431 #else
432     blockSize = status->st_blksize;
433 #endif
434     length = status->st_size;
435     buffer = (char *)malloc(blockSize);
436     if (!buffer) {
437         printf("malloc failed\n");
438         return UPDATE_ERROR;
439     }
440     tlen = htonl(length);
441     rx_Write(call, &tlen, sizeof(afs_int32));   /* send length on fetch */
442     while (!error && length) {
443         register int nbytes = (length > blockSize ? blockSize : length);
444         nbytes = read(fd, buffer, nbytes);
445         if (nbytes <= 0) {
446             fprintf(stderr, "File system read failed\n");
447             break;
448         }
449         if (rx_Write(call, buffer, nbytes) != nbytes)
450             break;
451         length -= nbytes;
452     }
453     if (buffer)
454         free(buffer);
455     if (length)
456         error = UPDATE_ERROR;
457     return error;
458 }
459
460 /* Enumerate dir (name) and write dir entry info into temp file. 
461  */
462 int
463 update_SendDirInfo(char *name,          /* Name of dir to enumerate */
464      register struct rx_call *call,     /* rx call */
465      register struct stat *status,      /* stat struct for dir */
466      char *origDir)             /* orig name of dir before being localized */
467 {
468     DIR *dirp;
469     struct dirent *dp;
470     FILE *stream;
471     struct stat tstatus;
472     char filename[MAXSIZE], dirInfoFile[MAXSIZE];
473     int fd, tfd, errcode, error, err;
474
475     error = 0;
476     dirp = opendir(name);
477     sprintf(dirInfoFile, "%s/upserver.tmp", gettmpdir());
478     stream = fopen(dirInfoFile, "w");
479     if (!stream) {
480         error = EIO;
481     } else {
482         while ((dp = readdir(dirp))) {
483             strcpy(filename, name);
484             strcat(filename, "/");
485             strcat(filename, dp->d_name);
486
487             tfd = open(filename, O_RDONLY, 0);
488             if (tfd < 0 || fstat(tfd, &tstatus) < 0) {
489                 printf("Failed to open %s\n", name);
490                 error = UPDATE_ERROR;
491                 goto fail;
492             }
493             if ((tstatus.st_mode & S_IFMT) != S_IFDIR) {        /* not a directory */
494                 char dirEntry[MAXSIZE];
495
496                 strcpy(dirEntry, origDir);
497                 strcat(dirEntry, "/");
498                 strcat(dirEntry, dp->d_name);
499                 err =
500                     fprintf(stream, "\"%s\" %u %u %u %u %u %u\n", dirEntry,
501                             (unsigned int)tstatus.st_mtime,
502                             (unsigned int)tstatus.st_size, tstatus.st_mode,
503                             tstatus.st_uid, tstatus.st_gid,
504                             (unsigned int)tstatus.st_atime);
505                 if (err < 0)
506                     error = EIO;
507             }
508             err = close(tfd);
509             if (err) {
510                 printf("could not close file %s \n", filename);
511                 error = UPDATE_ERROR;
512                 goto fail;
513             }
514         }
515     }
516   fail:
517     if (dirp)
518         closedir(dirp);
519     if (stream) {
520         if (ferror(stream))
521             if (!error)
522                 error = UPDATE_ERROR;
523         fclose(stream);
524     }
525     if (error == 0) {
526         fd = open(dirInfoFile, O_RDONLY, 0);
527         if (fd >= 0) {
528             fstat(fd, &tstatus);
529             errcode = update_SendFile(fd, call, &tstatus);
530             if (errcode)
531                 if (!error)
532                     error = UPDATE_ERROR;
533             close(fd);
534         }
535     }
536     unlink(dirInfoFile);
537     return error;
538 }
539
540
541 /* AddObject() - Adds the object to the list of exported objects after
542  *     converting to a local path.
543  *
544  * expPath : points to allocated storage in which the exportable path is 
545  *           passed back.
546  * dir     : dir name passed in for export 
547  *
548  */
549 static int
550 AddObject(char **expPath, char *dir)
551 {
552     int error;
553     struct stat statbuf;
554
555     /* construct a local path from a canonical (wire-format) path */
556     if ((error = ConstructLocalPath(dir, "/", expPath))) {
557         afs_com_err(whoami, error, "Unable to construct local path");
558         return error;
559     }
560
561     /* stat the object */
562     error = stat(*expPath, &statbuf);
563     if (error) {
564         afs_com_err(whoami, error, ";Can't stat object.");
565         return error;
566     }
567     /* now check if the object has an exportable (file/dir)  type */
568     if (!(statbuf.st_mode & S_IFDIR)) {
569         fprintf(stderr, "%s: Unacceptable object type for %s\n", whoami,
570                 *expPath);
571         return -1;
572     }
573
574     return 0;
575 }