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