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