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