5d3cb3e7b340d48b46b5776da0a7500ebd089037
[openafs.git] / src / libadmin / cfg / cfgclient.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 /* This file implements configuration functions in the following categories:
11  *   cfg_Client*()       - perform minimally necessary client configuration.
12  */
13
14 #include <afsconfig.h>
15 #include <afs/param.h>
16
17
18 #include <afs/stds.h>
19
20 #include <stddef.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <pthread.h>
26
27 #include <rx/rx.h>
28 #include <rx/rxstat.h>
29 #include <afs/afs_Admin.h>
30 #include <afs/afs_AdminErrors.h>
31 #include <afs/afs_utilAdmin.h>
32
33 #include <afs/dirpath.h>
34 #include <afs/cellconfig.h>
35 #include <afs/kautils.h>
36
37 #ifdef AFS_NT40_ENV
38 #include <windows.h>
39 #include <WINNT/afsreg.h>
40 #include <WINNT/afssw.h>
41 #include <cellservdb.h>
42 #endif
43
44 #include "cfginternal.h"
45 #include "afs_cfgAdmin.h"
46
47
48 /* Local declarations and definitions */
49
50 #define CSDB_OP_ADD  0          /* add a client CellServDB entry */
51 #define CSDB_OP_REM  1          /* remove a client CellServDB entry */
52
53 static int
54   ClientCellServDbUpdate(int updateOp, void *hostHandle, const char *cellName,
55                          const char *dbentry, afs_status_p st);
56
57 static int
58   CacheManagerStart(unsigned timeout, afs_status_p st);
59
60 static int
61   CacheManagerStop(unsigned timeout, afs_status_p st);
62
63
64
65
66 /* ---------------- Exported AFS Client functions ------------------ */
67
68
69 /*
70  * cfg_ClientQueryStatus() -- Query status of static client configuration
71  *     on host, i.e., status of required configuration files, etc.
72  *     Upon successful completion *configStP is set to the client
73  *     configuration status, with a value of zero (0) indicating that
74  *     the configuration is valid.
75  *
76  *     If client configuration is not valid then *cellNameP is set to NULL;
77  *     otherwise, *cellNameP is an allocated buffer containing client cell.
78  *
79  *     If client software (cache-manager) is not installed then *versionP is
80  *     undefined; otherwise *versionP is 34 for 3.4, 35 for 3.5, etc.
81  *
82  *     Note: Client configuration is checked even if the client software
83  *           is not installed.  This is useful for tools that require
84  *           client configuration information but NOT the actual
85  *           client (cache-manager); for example, the AFS Server Manager.
86  */
87 int ADMINAPI
88 cfg_ClientQueryStatus(const char *hostName,     /* name of host */
89                       short *isInstalledP,      /* client software installed */
90                       unsigned *versionP,       /* client software version */
91                       afs_status_p configStP,   /* client config status */
92                       char **cellNameP, /* client's cell */
93                       afs_status_p st)
94 {                               /* completion status */
95     int rc = 1;
96     afs_status_t tst2, tst = 0;
97     afs_status_t clientSt = 0;
98     char *clientCellName = NULL;
99     short cmInstalled = 0;
100     unsigned cmVersion = 0;
101
102     /* validate parameters */
103
104     if (hostName == NULL || *hostName == '\0') {
105         tst = ADMCFGHOSTNAMENULL;
106     } else if (strlen(hostName) > (MAXHOSTCHARS - 1)) {
107         tst = ADMCFGHOSTNAMETOOLONG;
108     } else if (isInstalledP == NULL) {
109         tst = ADMCFGINSTALLEDFLAGPNULL;
110     } else if (versionP == NULL) {
111         tst = ADMCFGVERSIONPNULL;
112     } else if (configStP == NULL) {
113         tst = ADMCFGCONFIGSTATUSPNULL;
114     } else if (cellNameP == NULL) {
115         tst = ADMCFGCELLNAMEPNULL;
116     }
117
118     /* remote configuration not yet supported; hostName must be local host */
119
120     if (tst == 0) {
121         short isLocal;
122
123         if (!cfgutil_HostNameIsLocal(hostName, &isLocal, &tst2)) {
124             tst = tst2;
125         } else if (!isLocal) {
126             tst = ADMCFGNOTSUPPORTED;
127         }
128     }
129
130     /* determine if client software (CM) is installed and if so what version */
131
132 #ifdef AFS_NT40_ENV
133     /* Windows - cache manager is a service */
134     if (tst == 0) {
135         DWORD svcState;
136
137         if (!cfgutil_WindowsServiceQuery
138             (AFSREG_CLT_SVC_NAME, &svcState, &tst2)) {
139             /* CM not installed, or insufficient privilege to check */
140             if (tst2 == ADMNOPRIV) {
141                 tst = tst2;
142             } else {
143                 cmInstalled = 0;
144             }
145         } else {
146             /* CM installed, get version */
147             unsigned major, minor, patch;
148
149             cmInstalled = 1;
150
151             if (afssw_GetClientVersion(&major, &minor, &patch)) {
152                 /* failed to retrieve version information */
153                 if (errno == EACCES) {
154                     tst = ADMNOPRIV;
155                 } else {
156                     tst = ADMCFGCLIENTVERSIONNOTREAD;
157                 }
158             } else {
159                 cmVersion = (major * 10) + minor;
160             }
161         }
162     }
163 #else
164     if (tst == 0) {
165         /* function not yet implemented for Unix */
166         tst = ADMCFGNOTSUPPORTED;
167     }
168 #endif /* AFS_NT40_ENV */
169
170
171     /* check static client configuration; not necessary that client
172      * software (CM) be installed for this information to be valid and useable.
173      */
174
175     if (tst == 0) {
176         struct afsconf_dir *confdir;
177
178         if ((confdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH)) == NULL) {
179             /* the client configuration appears to be missing/invalid */
180             clientSt = ADMCFGCLIENTBASICINFOINVALID;
181         } else {
182             struct afsconf_entry *cellentry;
183
184             if (confdir->cellName == NULL || *confdir->cellName == '\0') {
185                 /* no cell set for client */
186                 clientSt = ADMCFGCLIENTNOTINCELL;
187             } else {
188                 for (cellentry = confdir->entries; cellentry != NULL;
189                      cellentry = cellentry->next) {
190                     if (!strcasecmp
191                         (confdir->cellName, cellentry->cellInfo.name)) {
192                         break;
193                     }
194                 }
195
196                 if (cellentry == NULL) {
197                     clientSt = ADMCFGCLIENTCELLNOTINDB;
198                 } else if (cellentry->cellInfo.numServers <= 0) {
199                     clientSt = ADMCFGCLIENTCELLHASNODBENTRIES;
200                 }
201             }
202
203             if (tst == 0 && clientSt == 0) {
204                 /* everything looks good; malloc cell name buffer to return */
205                 clientCellName =
206                     (char *)malloc(strlen(cellentry->cellInfo.name) + 1);
207                 if (clientCellName == NULL) {
208                     tst = ADMNOMEM;
209                 } else {
210                     strcpy(clientCellName, cellentry->cellInfo.name);
211                 }
212             }
213
214             (void)afsconf_Close(confdir);
215         }
216     }
217
218     /* return result of query */
219
220     if (tst == 0) {
221         /* return client status and cell name */
222         *isInstalledP = cmInstalled;
223         *versionP = cmVersion;
224         *configStP = clientSt;
225
226         if (clientSt == 0) {
227             *cellNameP = clientCellName;
228         } else {
229             *cellNameP = NULL;
230         }
231     } else {
232         /* indicate failure */
233         rc = 0;
234
235         /* free cell name if allocated before failure */
236         if (clientCellName != NULL) {
237             free(clientCellName);
238         }
239     }
240     if (st != NULL) {
241         *st = tst;
242     }
243     return rc;
244 }
245
246
247 /*
248  * cfg_ClientSetCell() -- Define default client cell for host.
249  *
250  *     The cellDbHosts argument is a multistring containing the names of
251  *     the existing database servers already configured in the cell; this
252  *     multistring list can be obtained via cfg_CellServDbEnumerate().
253  *     If configuring the first server in a new cell then the cellDbHosts
254  *     list contains only the name of that host.
255  *
256  *     Warning: client (cache-manager) should be stopped prior to setting cell.
257  */
258 int ADMINAPI
259 cfg_ClientSetCell(void *hostHandle,     /* host config handle */
260                   const char *cellName, /* cell name */
261                   const char *cellDbHosts,      /* cell database hosts */
262                   afs_status_p st)
263 {                               /* completion status */
264     int rc = 1;
265     afs_status_t tst2, tst = 0;
266     cfg_host_p cfg_host = (cfg_host_p) hostHandle;
267
268     /* validate parameters */
269
270     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
271         tst = tst2;
272     } else if (cellName == NULL || *cellName == '\0') {
273         tst = ADMCFGCELLNAMENULL;
274     } else if (strlen(cellName) > (MAXCELLCHARS - 1)) {
275         tst = ADMCFGCELLNAMETOOLONG;
276     } else if (!cfgutil_HostHandleCellNameCompatible(cfg_host, cellName)) {
277         tst = ADMCFGCELLNAMECONFLICT;
278     } else if (cellDbHosts == NULL || *cellDbHosts == '\0') {
279         tst = ADMCFGCELLDBHOSTSNULL;
280     }
281
282     /* remote configuration not yet supported in this function */
283
284     if (tst == 0) {
285         if (!cfg_host->is_local) {
286             tst = ADMCFGNOTSUPPORTED;
287         }
288     }
289
290     /* define cell database hosts */
291
292 #ifdef AFS_NT40_ENV
293     if (tst == 0) {
294         CELLSERVDB clientDb;
295
296         if (!CSDB_ReadFile(&clientDb, AFSDIR_CLIENT_CELLSERVDB_FILEPATH)) {
297             tst = ADMCFGCLIENTCELLSERVDBNOTREAD;
298         } else {
299             CELLDBLINE *cellLinep = CSDB_FindCell(&clientDb, cellName);
300
301             if (cellLinep != NULL) {
302                 /* cell entry exists; remove host entries */
303                 if (!CSDB_RemoveCellServers(&clientDb, cellLinep)) {
304                     /* should never happen */
305                     tst = ADMCFGCLIENTCELLSERVDBEDITFAILED;
306                 }
307             } else {
308                 /* cell entry does not exist; add it */
309                 cellLinep = CSDB_AddCell(&clientDb, cellName, NULL, NULL);
310
311                 if (cellLinep == NULL) {
312                     tst = ADMNOMEM;
313                 }
314             }
315
316             if (tst == 0) {
317                 /* add new host entries to cell */
318                 const char *dbHost = cellDbHosts;
319                 int dbHostCount = 0;
320
321                 while (*dbHost != '\0' && tst == 0) {
322                     size_t dbHostLen = strlen(dbHost);
323                     const char *dbHostAddrStr;
324
325                     if (dbHostLen > (MAXHOSTCHARS - 1)) {
326                         tst = ADMCFGHOSTNAMETOOLONG;
327                     } else if (dbHostCount >= MAXHOSTSPERCELL) {
328                         tst = ADMCFGCELLDBHOSTCOUNTTOOLARGE;
329                     } else
330                         if (!cfgutil_HostNameGetAddressString
331                             (dbHost, &dbHostAddrStr, &tst2)) {
332                         tst = tst2;
333                     } else
334                         if (CSDB_AddCellServer
335                             (&clientDb, cellLinep, dbHostAddrStr,
336                              dbHost) == NULL) {
337                         tst = ADMNOMEM;
338                     } else {
339                         dbHostCount++;
340                         dbHost += dbHostLen + 1;
341                     }
342                 }
343
344                 if (tst == 0) {
345                     /* edit successful; write CellServDB */
346                     if (!CSDB_WriteFile(&clientDb)) {
347                         tst = ADMCFGCLIENTCELLSERVDBNOTWRITTEN;
348                     }
349                 }
350             }
351
352             CSDB_FreeFile(&clientDb);
353         }
354     }
355 #else
356     if (tst == 0) {
357         /* function not yet implemented for Unix */
358         tst = ADMCFGNOTSUPPORTED;
359     }
360 #endif /* AFS_NT40_ENV */
361
362
363     /* define default client cell */
364
365 #ifdef AFS_NT40_ENV
366     if (tst == 0) {
367         if (afssw_SetClientCellName(cellName)) {
368             /* failed to set cell name in registry (ThisCell equivalent) */
369             if (errno == EACCES) {
370                 tst = ADMNOPRIV;
371             } else {
372                 tst = ADMCFGCLIENTTHISCELLNOTWRITTEN;
373             }
374         }
375     }
376 #else
377     if (tst == 0) {
378         /* function not yet implemented for Unix */
379         tst = ADMCFGNOTSUPPORTED;
380     }
381 #endif /* AFS_NT40_ENV */
382
383
384     /* help any underlying packages adjust to cell change */
385
386     if (tst == 0) {
387         int rc;
388
389         if ((rc = ka_CellConfig(AFSDIR_CLIENT_ETC_DIRPATH)) != 0) {
390             tst = rc;
391         }
392     }
393
394     if (tst != 0) {
395         rc = 0;
396     }
397     if (st != NULL) {
398         *st = tst;
399     }
400     return rc;
401 }
402
403
404 /*
405  * cfg_ClientCellServDbAdd() -- Add entry to client CellServDB on host.
406  */
407 int ADMINAPI
408 cfg_ClientCellServDbAdd(void *hostHandle,       /* host config handle */
409                         const char *cellName,   /* cell name */
410                         const char *dbentry,    /* cell database entry */
411                         afs_status_p st)
412 {                               /* completion status */
413     return ClientCellServDbUpdate(CSDB_OP_ADD, hostHandle, cellName, dbentry,
414                                   st);
415 }
416
417
418 /*
419  * cfg_ClientCellServDbRemove() -- Remove entry from client CellServDB
420  *     on host.
421  */
422 int ADMINAPI
423 cfg_ClientCellServDbRemove(void *hostHandle,    /* host config handle */
424                            const char *cellName,        /* cell name */
425                            const char *dbentry, /* cell database entry */
426                            afs_status_p st)
427 {                               /* completion status */
428     return ClientCellServDbUpdate(CSDB_OP_REM, hostHandle, cellName, dbentry,
429                                   st);
430 }
431
432
433 /*
434  * cfg_ClientStop() -- Stop the client (cache manager) on host.
435  *
436  *     Timeout is the maximum time (in seconds) to wait for client to stop.
437  */
438 int ADMINAPI
439 cfg_ClientStop(void *hostHandle,        /* host config handle */
440                unsigned int timeout,    /* timeout in seconds */
441                afs_status_p st)
442 {                               /* completion status */
443     int rc = 1;
444     afs_status_t tst2, tst = 0;
445     cfg_host_p cfg_host = (cfg_host_p) hostHandle;
446
447     /* validate parameters */
448
449     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
450         tst = tst2;
451     }
452
453     /* remote configuration not yet supported in this function */
454
455     if (tst == 0) {
456         if (!cfg_host->is_local) {
457             tst = ADMCFGNOTSUPPORTED;
458         }
459     }
460
461     /* stop client */
462
463     if (tst == 0) {
464         if (!CacheManagerStop(timeout, &tst2)) {
465             tst = tst2;
466         }
467     }
468
469     if (tst != 0) {
470         /* indicate failure */
471         rc = 0;
472     }
473     if (st != NULL) {
474         *st = tst;
475     }
476     return rc;
477 }
478
479
480 /*
481  * cfg_ClientStart() -- Start the client (cache manager) on host.
482  *
483  *     Timeout is the maximum time (in seconds) to wait for client to start.
484  */
485 int ADMINAPI
486 cfg_ClientStart(void *hostHandle,       /* host config handle */
487                 unsigned int timeout,   /* timeout in seconds */
488                 afs_status_p st)
489 {                               /* completion status */
490     int rc = 1;
491     afs_status_t tst2, tst = 0;
492     cfg_host_p cfg_host = (cfg_host_p) hostHandle;
493
494     /* validate parameters */
495
496     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
497         tst = tst2;
498     }
499
500     /* remote configuration not yet supported in this function */
501
502     if (tst == 0) {
503         if (!cfg_host->is_local) {
504             tst = ADMCFGNOTSUPPORTED;
505         }
506     }
507
508     /* start client */
509
510     if (tst == 0) {
511         if (!CacheManagerStart(timeout, &tst2)) {
512             tst = tst2;
513         }
514     }
515
516     if (tst != 0) {
517         /* indicate failure */
518         rc = 0;
519     }
520     if (st != NULL) {
521         *st = tst;
522     }
523     return rc;
524 }
525
526
527
528 /* ---------------- Local functions ------------------ */
529
530
531 /*
532  * ClientCellServDbUpdate() -- add or remove a client CellServDB entry.
533  *
534  *     Common function implementing cfg_ClientCellServDb{Add/Remove}().
535  */
536 static int
537 ClientCellServDbUpdate(int updateOp, void *hostHandle, const char *cellName,
538                        const char *dbentry, afs_status_p st)
539 {
540     int rc = 1;
541     afs_status_t tst2, tst = 0;
542     cfg_host_p cfg_host = (cfg_host_p) hostHandle;
543     char dbentryFull[MAXHOSTCHARS];
544
545     /* validate parameters and resolve dbentry to fully qualified name */
546
547     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
548         tst = tst2;
549     } else if (cellName == NULL || *cellName == '\0') {
550         tst = ADMCFGCELLNAMENULL;
551     } else if (strlen(cellName) > (MAXCELLCHARS - 1)) {
552         tst = ADMCFGCELLNAMETOOLONG;
553     } else if (dbentry == NULL || *dbentry == '\0') {
554         tst = ADMCFGHOSTNAMENULL;
555     } else if (strlen(dbentry) > (MAXHOSTCHARS - 1)) {
556         tst = ADMCFGHOSTNAMETOOLONG;
557     } else if (!cfgutil_HostNameGetFull(dbentry, dbentryFull, &tst2)) {
558         tst = tst2;
559     }
560
561     /* remote configuration not yet supported in this function */
562
563     if (tst == 0) {
564         if (!cfg_host->is_local) {
565             tst = ADMCFGNOTSUPPORTED;
566         }
567     }
568
569     /* modify local client CellServDB entry for specified cell */
570
571 #ifdef AFS_NT40_ENV
572     if (tst == 0) {
573         CELLSERVDB clientDb;
574
575         if (!CSDB_ReadFile(&clientDb, AFSDIR_CLIENT_CELLSERVDB_FILEPATH)) {
576             tst = ADMCFGCLIENTCELLSERVDBNOTREAD;
577         } else {
578             CELLDBLINE *cellLinep = CSDB_FindCell(&clientDb, cellName);
579             CELLDBLINE *serverLinep = NULL;
580             int serverLineCount = 0;
581
582             if (cellLinep != NULL) {
583                 /* found cellName, now find server to add/remove */
584                 CELLDBLINE *workingLinep;
585
586                 for (workingLinep = cellLinep->pNext; workingLinep != NULL;
587                      workingLinep = workingLinep->pNext) {
588                     CELLDBLINEINFO lineInfo;
589
590                     if (!CSDB_CrackLine(&lineInfo, workingLinep->szLine)) {
591                         /* not a server (or cell) line; perhaps a comment */
592                         continue;
593                     } else if (lineInfo.szCell[0] != '\0') {
594                         /* hit a new cell line */
595                         break;
596                     } else {
597                         /* found a server line; check if is host of interest */
598                         short isValid;
599                         int dbentryAddr = ntohl(lineInfo.ipServer);
600
601                         serverLineCount++;
602
603                         if (!cfgutil_HostAddressIsValid
604                             (dbentryFull, dbentryAddr, &isValid, &tst2)) {
605                             tst = tst2;
606                             break;
607                         } else if (isValid) {
608                             /* found server of interest */
609                             serverLinep = workingLinep;
610                             break;
611                         }
612                     }
613                 }
614             }
615
616             if (tst == 0) {
617                 if (updateOp == CSDB_OP_ADD && serverLinep == NULL) {
618                     if (cellLinep == NULL) {
619                         cellLinep =
620                             CSDB_AddCell(&clientDb, cellName, NULL, NULL);
621                     }
622
623                     if (cellLinep == NULL) {
624                         tst = ADMNOMEM;
625                     } else if (serverLineCount >= MAXHOSTSPERCELL) {
626                         tst = ADMCFGCLIENTCELLSERVDBNOSPACE;
627                     } else {
628                         const char *dbentryAddrStr;
629
630                         if (!cfgutil_HostNameGetAddressString
631                             (dbentryFull, &dbentryAddrStr, &tst2)) {
632                             tst = tst2;
633                         } else {
634                             serverLinep =
635                                 CSDB_AddCellServer(&clientDb, cellLinep,
636                                                    dbentryAddrStr,
637                                                    dbentryFull);
638                             if (serverLinep == NULL) {
639                                 tst = ADMNOMEM;
640                             }
641                         }
642                     }
643                 } else if (updateOp == CSDB_OP_REM && serverLinep != NULL) {
644                     (void)CSDB_RemoveLine(&clientDb, serverLinep);
645                 }
646
647                 if (tst == 0) {
648                     if (!CSDB_WriteFile(&clientDb)) {
649                         tst = ADMCFGCLIENTCELLSERVDBNOTWRITTEN;
650                     }
651                 }
652             }
653
654             CSDB_FreeFile(&clientDb);
655         }
656     }
657 #else
658     if (tst == 0) {
659         /* function not yet implemented for Unix */
660         tst = ADMCFGNOTSUPPORTED;
661     }
662 #endif /* AFS_NT40_ENV */
663
664     if (tst != 0) {
665         /* indicate failure */
666         rc = 0;
667     }
668     if (st != NULL) {
669         *st = tst;
670     }
671     return rc;
672 }
673
674
675 /*
676  * CacheManagerStart() -- Start the local AFS cache manager.
677  *
678  *     Timeout is the maximum time (in seconds) to wait for the CM to start.
679  *
680  * RETURN CODES: 1 success, 0 failure (st indicates why)
681  */
682 static int
683 CacheManagerStart(unsigned timeout, afs_status_p st)
684 {
685     int rc = 1;
686 #ifdef AFS_NT40_ENV
687     afs_status_t tst2;
688 #endif
689     afs_status_t tst = 0;
690
691 #ifdef AFS_NT40_ENV
692     /* Windows - cache manager is a service */
693     short wasRunning;
694
695     if (!cfgutil_WindowsServiceStart
696         (AFSREG_CLT_SVC_NAME, 0, NULL, timeout, &wasRunning, &tst2)) {
697         tst = tst2;
698     }
699 #else
700     /* function not yet implemented for Unix */
701     tst = ADMCFGNOTSUPPORTED;
702 #endif /* AFS_NT40_ENV */
703
704     if (tst != 0) {
705         /* indicate failure */
706         rc = 0;
707     }
708     if (st != NULL) {
709         *st = tst;
710     }
711     return rc;
712 }
713
714
715 /*
716  * CacheManagerStop() -- Stop the local AFS cache manager.
717  *
718  *     Timeout is the maximum time (in seconds) to wait for the CM to stop.
719  *
720  * RETURN CODES: 1 success, 0 failure (st indicates why)
721  */
722 static int
723 CacheManagerStop(unsigned timeout, afs_status_p st)
724 {
725     int rc = 1;
726 #ifdef AFS_NT40_ENV
727     afs_status_t tst2;
728 #endif
729     afs_status_t tst = 0;
730
731 #ifdef AFS_NT40_ENV
732     /* Windows - cache manager is a service */
733     short wasStopped;
734
735     if (!cfgutil_WindowsServiceStop
736         (AFSREG_CLT_SVC_NAME, timeout, &wasStopped, &tst2)) {
737         tst = tst2;
738     }
739 #else
740     /* function not yet implemented for Unix */
741     tst = ADMCFGNOTSUPPORTED;
742 #endif /* AFS_NT40_ENV */
743
744     if (tst != 0) {
745         /* indicate failure */
746         rc = 0;
747     }
748     if (st != NULL) {
749         *st = tst;
750     }
751     return rc;
752 }