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