db8a1256c56389b4347b11e530bfbf77bf7367b5
[openafs.git] / src / WINNT / afssvrmgr / alert.cpp
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 extern "C" {
11 #include <afs/param.h>
12 #include <afs/stds.h>
13 }
14
15 #include "svrmgr.h"
16 #include "alert.h"
17 #include "creds.h"
18
19
20 /*
21  * DEFINITIONS ________________________________________________________________
22  *
23  */
24
25
26 /*
27  * VARIABLES __________________________________________________________________
28  *
29  */
30
31
32 /*
33  * PROTOTYPES _________________________________________________________________
34  *
35  */
36
37 BOOL Alert_Scout_CheckServer (LPSERVER lpServer);
38
39 void Alert_BeginUpdate (LPIDENT lpi, LPSERVER *ppServer);
40 void Alert_EndUpdate (LPIDENT lpi, LPSERVER lpServer);
41
42 void Alert_RemoveFunc (LPOBJECTALERTS lpoa, size_t iAlert);
43 LPTSTR Alert_GetDescriptionFunc (LPIDENT lpiPrimary, size_t iAlertPrimary, LPIDENT lpiServer, BOOL fFull);
44 LPTSTR Alert_GetRemedyFunc (LPIDENT lpiPrimary, size_t iAlertPrimary, LPIDENT lpiServer);
45 LPTSTR Alert_GetButtonFunc (LPIDENT lpiPrimary, size_t iAlertPrimary, LPIDENT lpiServer);
46
47 DWORD WINAPI Alert_ScoutProc (LPVOID lp);
48 void Alert_Scout_SetUpToDate (LPOBJECTALERTS lpoa);
49
50
51 /*
52  * ROUTINES ___________________________________________________________________
53  *
54  */
55
56 LPOBJECTALERTS Alert_GetObjectAlerts (LPIDENT lpi, BOOL fAlwaysServer, ULONG *pStatus)
57 {
58    LPOBJECTALERTS lpoa = NULL;
59
60    if (fAlwaysServer || lpi->fIsServer())
61       {
62       LPSERVER_PREF lpsp;
63       if ((lpsp = (LPSERVER_PREF)lpi->GetServer()->GetUserParam()) != NULL)
64          {
65          lpoa = &lpsp->oa;
66          }
67       }
68    else if (lpi->fIsService())
69       {
70       LPSERVICE_PREF lpsp;
71       if ((lpsp = (LPSERVICE_PREF)lpi->GetUserParam()) != NULL)
72          {
73          lpoa = &lpsp->oa;
74          }
75       }
76    else if (lpi->fIsAggregate())
77       {
78       LPAGGREGATE_PREF lpap;
79       if ((lpap = (LPAGGREGATE_PREF)lpi->GetUserParam()) != NULL)
80          {
81          lpoa = &lpap->oa;
82          }
83       }
84    else if (lpi->fIsFileset())
85       {
86       LPFILESET_PREF lpfp;
87       if ((lpfp = (LPFILESET_PREF)lpi->GetUserParam()) != NULL)
88          {
89          lpoa = &lpfp->oa;
90          }
91       }
92
93    return lpoa;
94 }
95
96
97 void Alert_BeginUpdate (LPIDENT lpi, LPSERVER *ppServer)
98 {
99    if (lpi->fIsServer())
100       {
101       *ppServer = NULL;
102       }
103    else
104       {
105       *ppServer = lpi->OpenServer();
106       }
107 }
108
109
110 void Alert_EndUpdate (LPIDENT lpi, LPSERVER lpServer)
111 {
112    // If we just updated some aggregate, fileset or service, then the
113    // associated server's secondary alerts are probably out-of-date.
114    // Update them.
115    //
116    if (lpServer != NULL)
117       {
118       LPOBJECTALERTS lpoaServer = Alert_GetObjectAlerts (lpServer->GetIdentifier());
119       LPOBJECTALERTS lpoaChild  = Alert_GetObjectAlerts (lpi);
120
121       if (lpoaServer)
122          {
123          for (size_t iAlert = 0; iAlert < lpoaServer->nAlerts; )
124             {
125             if ( (lpoaServer->aAlerts[ iAlert ].alert == alertSECONDARY) &&
126                  (lpoaServer->aAlerts[ iAlert ].aiSECONDARY.lpiSecondary == lpi) )
127                {
128                Alert_RemoveFunc (lpoaServer, iAlert);
129                }
130             else
131                {
132                ++iAlert;
133                }
134             }
135          }
136
137       if (lpoaServer && lpoaChild)
138          {
139          BOOL fNeedBadCredsWarning = FALSE;
140          BOOL fHaveBadCredsWarning = FALSE;
141
142          for (size_t iAlert = 0; iAlert < lpoaServer->nAlerts; ++iAlert)
143             {
144             if (lpoaServer->aAlerts[ iAlert ].alert == alertSECONDARY)
145                {
146                ALERT alert = Alert_GetAlert (lpoaServer->aAlerts[ iAlert ].aiSECONDARY.lpiSecondary,
147                                              lpoaServer->aAlerts[ iAlert ].aiSECONDARY.iSecondary);
148                if (alert == alertNO_SVRENT)
149                   fNeedBadCredsWarning = TRUE;
150                }
151             }
152          for (iAlert = 0; iAlert < lpoaChild->nAlerts; ++iAlert)
153             {
154             if (lpoaChild->aAlerts[ iAlert ].alert == alertNO_SVRENT)
155                fNeedBadCredsWarning = TRUE;
156             }
157          if (lpoaServer->nAlerts &&
158              lpoaServer->aAlerts[ 0 ].alert == alertBADCREDS)
159             {
160             fHaveBadCredsWarning = TRUE;
161             }
162
163          if (fNeedBadCredsWarning)
164             {
165             fNeedBadCredsWarning = !CheckCredentials (FALSE);
166             }
167
168          if (fHaveBadCredsWarning && !fNeedBadCredsWarning)
169             {
170             Alert_RemoveFunc (lpoaServer, 0);
171             }
172          else if (fNeedBadCredsWarning && !fHaveBadCredsWarning)
173             {
174             for (iAlert = min( lpoaServer->nAlerts, nAlertsMAX-1 );
175                  iAlert > 0;
176                  --iAlert)
177                {
178                memcpy (&lpoaServer->aAlerts[ iAlert ], &lpoaServer->aAlerts[ iAlert-1 ], sizeof(ALERTINFO));
179                }
180             lpoaServer->aAlerts[0].alert = alertBADCREDS;
181             lpoaServer->nAlerts = min( nAlertsMAX, lpoaServer->nAlerts+1 );
182             }
183
184          for (iAlert = 0; iAlert < lpoaChild->nAlerts; ++iAlert)
185             {
186             if (lpoaServer->nAlerts < nAlertsMAX)
187                {
188                lpoaServer->aAlerts[ lpoaServer->nAlerts ].alert = alertSECONDARY;
189                lpoaServer->aAlerts[ lpoaServer->nAlerts ].aiSECONDARY.lpiSecondary = lpi;
190                lpoaServer->aAlerts[ lpoaServer->nAlerts ].aiSECONDARY.iSecondary = iAlert;
191                lpoaServer->nAlerts ++;
192                }
193             }
194          }
195
196       lpServer->Close();
197       }
198 }
199
200
201 void Alert_SetDefaults (LPOBJECTALERTS lpoa)
202 {
203    if (lpoa != NULL)
204       {
205       memset (lpoa, 0x00, sizeof(OBJECTALERTS));
206       lpoa->cTickRefresh = DEFAULT_SCOUT_REFRESH_RATE;
207       }
208 }
209
210
211 void Alert_Initialize (LPOBJECTALERTS lpoa)
212 {
213    if (lpoa != NULL)
214       {
215       lpoa->dwTickNextTest = 0;
216       lpoa->dwTickNextRefresh = 0;
217       lpoa->nAlerts = 0;
218       }
219 }
220
221
222 void Alert_RemoveSecondary (LPIDENT lpiChild)
223 {
224    BOOL fChangedAlerts = FALSE;
225
226    LPOBJECTALERTS lpoa;
227    if ((lpoa = Alert_GetObjectAlerts (lpiChild, TRUE)) != NULL)
228       {
229       for (size_t iAlert = 0; iAlert < lpoa->nAlerts; )
230          {
231          if ( (lpoa->aAlerts[ iAlert ].alert == alertSECONDARY) &&
232               (lpoa->aAlerts[ iAlert ].aiSECONDARY.lpiSecondary == lpiChild) )
233             {
234             Alert_RemoveFunc (lpoa, iAlert);
235             fChangedAlerts = TRUE;
236             }
237          else
238             {
239             ++iAlert;
240             }
241          }
242
243
244       BOOL fNeedBadCredsWarning = FALSE;
245
246       for (iAlert = 0; iAlert < lpoa->nAlerts; ++iAlert)
247          {
248          if (lpoa->aAlerts[ iAlert ].alert == alertSECONDARY)
249             {
250             ALERT alert = Alert_GetAlert (lpoa->aAlerts[ iAlert ].aiSECONDARY.lpiSecondary,
251                                           lpoa->aAlerts[ iAlert ].aiSECONDARY.iSecondary);
252             if (alert == alertNO_SVRENT)
253                fNeedBadCredsWarning = TRUE;
254             }
255          }
256
257       if ( (!fNeedBadCredsWarning) &&
258            (lpoa->nAlerts && (lpoa->aAlerts[ 0 ].alert == alertBADCREDS)) )
259          {
260          Alert_RemoveFunc (lpoa, 0);
261          fChangedAlerts = TRUE;
262          }
263       }
264
265    if (fChangedAlerts)
266       {
267       PostNotification (evtAlertsChanged, lpiChild->GetServer());
268       PostNotification (evtAlertsChanged, lpiChild);
269       }
270 }
271
272
273 void Alert_Remove (LPIDENT lpi, size_t iAlert)
274 {
275    LPOBJECTALERTS lpoa;
276
277    if ((lpoa = Alert_GetObjectAlerts (lpi)) != NULL)
278       {
279       if (iAlert < lpoa->nAlerts)
280          {
281          LPSERVER lpServer;
282          Alert_BeginUpdate (lpi, &lpServer);
283
284          Alert_RemoveFunc (lpoa, iAlert);
285
286          Alert_EndUpdate (lpi, lpServer);
287          }
288       }
289 }
290
291
292 void Alert_RemoveFunc (LPOBJECTALERTS lpoa, size_t iAlert)
293 {
294    if (iAlert < lpoa->nAlerts-1)
295       {
296       memcpy (&lpoa->aAlerts[ iAlert ], &lpoa->aAlerts[ lpoa->nAlerts-1 ], sizeof(ALERTINFO));
297       }
298    lpoa->nAlerts --;
299 }
300
301
302 void Alert_AddPrimary (LPIDENT lpi, LPALERTINFO lpai)
303 {
304    LPOBJECTALERTS lpoa;
305
306    if ((lpoa = Alert_GetObjectAlerts (lpi)) != NULL)
307       {
308       if (lpoa->nAlerts < nAlertsMAX)
309          {
310          LPSERVER lpServer;
311          Alert_BeginUpdate (lpi, &lpServer);
312
313          memcpy (&lpoa->aAlerts[ lpoa->nAlerts ], lpai, sizeof(ALERTINFO));
314          lpoa->nAlerts ++;
315
316          Alert_EndUpdate (lpi, lpServer);
317          }
318       }
319 }
320
321
322 void Alert_Scout_ServerStatus (LPIDENT lpi, ULONG status)
323 {
324    LPOBJECTALERTS lpoa;
325    if ((lpoa = Alert_GetObjectAlerts (lpi)) != NULL)
326       {
327       BOOL fChanged = FALSE;
328
329       for (size_t iAlert = 0; iAlert < lpoa->nAlerts; ++iAlert)
330          {
331          if (lpoa->aAlerts[ iAlert ].alert == alertTIMEOUT)
332             {
333             fChanged = TRUE;
334             Alert_RemoveFunc (lpoa, iAlert);
335             break;
336             }
337          }
338
339       if (status != 0)
340          {
341          size_t iInsert = 0;
342          if (lpoa->nAlerts && (lpoa->aAlerts[0].alert == alertBADCREDS))
343             iInsert = 1;
344
345          for (size_t iHole = iInsert; iHole < lpoa->nAlerts; ++iHole)
346             {
347             if (lpoa->aAlerts[ iHole ].alert == alertINVALID)
348                break;
349             }
350          if (iHole < nAlertsMAX)
351             {
352             for (size_t iTarget = iHole; iTarget > iInsert; --iTarget)
353                {
354                memcpy (&lpoa->aAlerts[ iTarget ], &lpoa->aAlerts[ iTarget-1 ], sizeof(ALERTINFO));
355                }
356
357             lpoa->nAlerts ++;
358             lpoa->aAlerts[ iInsert ].alert = alertTIMEOUT;
359             lpoa->aAlerts[ iInsert ].aiTIMEOUT.status = status;
360             lpoa->aAlerts[ iInsert ].aiTIMEOUT.stLastAttempt;
361             GetSystemTime (&lpoa->aAlerts[ iInsert ].aiTIMEOUT.stLastAttempt);
362
363             fChanged = TRUE;
364             }
365          }
366
367       if (fChanged)
368          {
369          PostNotification (evtAlertsChanged, lpi);
370          }
371       }
372 }
373
374
375 size_t Alert_GetCount (LPIDENT lpi)
376 {
377    LPOBJECTALERTS lpoa;
378
379    if ((lpoa = Alert_GetObjectAlerts (lpi)) == NULL)
380       return 0;
381
382    return lpoa->nAlerts;
383 }
384
385
386 ALERT Alert_GetAlert (LPIDENT lpi, size_t iAlert)
387 {
388    LPOBJECTALERTS lpoa;
389
390    if ((lpoa = Alert_GetObjectAlerts (lpi)) == NULL)
391       return alertINVALID;
392
393    if (iAlert > lpoa->nAlerts)
394       return alertINVALID;
395
396    if (lpoa->aAlerts[ iAlert ].alert == alertSECONDARY)
397       {
398       return Alert_GetAlert (lpoa->aAlerts[ iAlert ].aiSECONDARY.lpiSecondary,
399                              lpoa->aAlerts[ iAlert ].aiSECONDARY.iSecondary);
400       }
401
402    return lpoa->aAlerts[ iAlert ].alert;
403 }
404
405
406 LPIDENT Alert_GetIdent (LPIDENT lpi, size_t iAlert)
407 {
408    LPOBJECTALERTS lpoa;
409
410    if ((lpoa = Alert_GetObjectAlerts (lpi)) == NULL)
411       return NULL;
412
413    if (iAlert > lpoa->nAlerts)
414       return NULL;
415
416    if (lpoa->aAlerts[ iAlert ].alert == alertSECONDARY)
417       {
418       return lpoa->aAlerts[ iAlert ].aiSECONDARY.lpiSecondary;
419       }
420
421    return lpi;
422 }
423
424
425 LPTSTR Alert_GetQuickDescription (LPIDENT lpi)
426 {
427    LPTSTR pszStatus = NULL;
428
429    size_t cAlerts;
430    if ((cAlerts = Alert_GetCount (lpi)) <= 1)
431       pszStatus = Alert_GetDescription (lpi, 0, FALSE);
432    else if (lpi->fIsServer())
433       pszStatus = FormatString (IDS_SERVER_MULTIPLE_PROBLEMS, TEXT("%lu"), cAlerts);
434    else if (lpi->fIsService())
435       pszStatus = FormatString (IDS_SERVICE_MULTIPLE_PROBLEMS, TEXT("%lu"), cAlerts);
436    else if (lpi->fIsAggregate())
437       pszStatus = FormatString (IDS_AGGREGATE_MULTIPLE_PROBLEMS, TEXT("%lu"), cAlerts);
438    else if (lpi->fIsFileset())
439       pszStatus = FormatString (IDS_FILESET_MULTIPLE_PROBLEMS, TEXT("%lu"), cAlerts);
440
441    return pszStatus;
442 }
443
444
445 LPTSTR Alert_GetDescription (LPIDENT lpi, size_t iAlert, BOOL fFull)
446 {
447    LPOBJECTALERTS lpoa;
448
449    if ((lpoa = Alert_GetObjectAlerts (lpi)) == NULL)
450       return NULL;
451
452    if (!lpoa->nAlerts && lpi->fIsServer())
453       {
454       LPSERVER_PREF lpsp;
455       if ((lpsp = (LPSERVER_PREF)lpi->GetUserParam()) != NULL)
456          {
457          if (!lpsp->fIsMonitored)
458             {
459             TCHAR szName[ cchNAME ];
460             lpi->GetServerName (szName);
461             return FormatString (IDS_ALERT_DESCSHORT_UNMONITORED, TEXT("%s"), szName);
462             }
463          }
464       }
465
466    if (iAlert >= lpoa->nAlerts)
467       return NULL;
468
469    if (lpoa->aAlerts[ iAlert ].alert == alertSECONDARY)
470       {
471       return Alert_GetDescriptionFunc (lpoa->aAlerts[ iAlert ].aiSECONDARY.lpiSecondary,
472                                        lpoa->aAlerts[ iAlert ].aiSECONDARY.iSecondary,
473                                        lpi,
474                                        fFull);
475       }
476
477    return Alert_GetDescriptionFunc (lpi, iAlert, NULL, fFull);
478 }
479
480
481 LPTSTR Alert_GetRemedy (LPIDENT lpi, size_t iAlert)
482 {
483    LPOBJECTALERTS lpoa;
484
485    if ((lpoa = Alert_GetObjectAlerts (lpi)) == NULL)
486       return NULL;
487
488    if (iAlert >= lpoa->nAlerts)
489       return NULL;
490
491    if (lpoa->aAlerts[ iAlert ].alert == alertSECONDARY)
492       {
493       return Alert_GetRemedyFunc (lpoa->aAlerts[ iAlert ].aiSECONDARY.lpiSecondary,
494                                   lpoa->aAlerts[ iAlert ].aiSECONDARY.iSecondary,
495                                   lpi);
496       }
497
498    return Alert_GetRemedyFunc (lpi, iAlert, NULL);
499 }
500
501
502 LPTSTR Alert_GetButton (LPIDENT lpi, size_t iAlert)
503 {
504    LPOBJECTALERTS lpoa;
505
506    if ((lpoa = Alert_GetObjectAlerts (lpi)) == NULL)
507       return NULL;
508
509    if (iAlert >= lpoa->nAlerts)
510       return NULL;
511
512    if (lpoa->aAlerts[ iAlert ].alert == alertSECONDARY)
513       {
514       return Alert_GetButtonFunc (lpoa->aAlerts[ iAlert ].aiSECONDARY.lpiSecondary,
515                                   lpoa->aAlerts[ iAlert ].aiSECONDARY.iSecondary,
516                                   lpi);
517       }
518
519    return Alert_GetButtonFunc (lpi, iAlert, NULL);
520 }
521
522
523 LPTSTR Alert_GetDescriptionFunc (LPIDENT lpiPrimary, size_t iAlertPrimary, LPIDENT lpiServer, BOOL fFull)
524 {
525    LPOBJECTALERTS lpoa;
526    if ((lpoa = Alert_GetObjectAlerts (lpiPrimary)) != NULL)
527       {
528       int ids;
529       TCHAR szServer[ cchRESOURCE ];
530       TCHAR szService[ cchRESOURCE ];
531       TCHAR szAggregate[ cchRESOURCE ];
532       TCHAR szFileset[ cchRESOURCE ];
533
534       switch (lpoa->aAlerts[ iAlertPrimary ].alert)
535          {
536          case alertTIMEOUT:
537             ids = (fFull) ? IDS_ALERT_DESCFULL_TIMEOUT : IDS_ALERT_DESCSHORT_TIMEOUT;
538             lpiPrimary->GetServerName (szServer);
539             return FormatString (ids, TEXT("%s%t%e"), szServer, &lpoa->aAlerts[ iAlertPrimary ].aiTIMEOUT.stLastAttempt, lpoa->aAlerts[ iAlertPrimary ].aiTIMEOUT.status);
540
541          case alertFULL:
542             lpiPrimary->GetServerName (szServer);
543             lpiPrimary->GetAggregateName (szAggregate);
544             if (lpiPrimary->fIsAggregate())
545                {
546                ids = (fFull) ? IDS_ALERT_DESCFULL_AGG_FULL : IDS_ALERT_DESCSHORT_AGG_FULL;
547                return FormatString (ids, TEXT("%s%s%d%.1B"), szServer, szAggregate, lpoa->aAlerts[ iAlertPrimary ].aiFULL.perWarning, 1024.0 * (double)lpoa->aAlerts[ iAlertPrimary ].aiFULL.ckWarning);
548                }
549             else if (lpiPrimary->fIsFileset())
550                {
551                ids = (fFull) ? IDS_ALERT_DESCFULL_SET_FULL : IDS_ALERT_DESCSHORT_SET_FULL;
552                lpiPrimary->GetFilesetName (szFileset);
553                return FormatString (ids, TEXT("%s%s%s%d%.1B"), szServer, szAggregate, szFileset, lpoa->aAlerts[ iAlertPrimary ].aiFULL.perWarning, 1024.0 * (double)lpoa->aAlerts[ iAlertPrimary ].aiFULL.ckWarning);
554                }
555             break;
556
557          case alertNO_VLDBENT:
558             ids = (fFull) ? IDS_ALERT_DESCFULL_NO_VLDBENT : IDS_ALERT_DESCSHORT_NO_VLDBENT;
559             lpiPrimary->GetServerName (szServer);
560             lpiPrimary->GetAggregateName (szAggregate);
561             lpiPrimary->GetFilesetName (szFileset);
562             return FormatString (ids, TEXT("%s%s%s"), szServer, szAggregate, szFileset);
563
564          case alertNO_SVRENT:
565             if (lpiPrimary->fIsFileset())
566                {
567                ids = (fFull) ? IDS_ALERT_DESCFULL_NO_SVRENT_SET : IDS_ALERT_DESCSHORT_NO_SVRENT_SET;
568                lpiPrimary->GetServerName (szServer);
569                lpiPrimary->GetAggregateName (szAggregate);
570                lpiPrimary->GetFilesetName (szFileset);
571                return FormatString (ids, TEXT("%s%s%s"), szServer, szAggregate, szFileset);
572                }
573             else
574                {
575                ids = (fFull) ? IDS_ALERT_DESCFULL_NO_SVRENT_AGG : IDS_ALERT_DESCSHORT_NO_SVRENT_AGG;
576                lpiPrimary->GetServerName (szServer);
577                lpiPrimary->GetAggregateName (szAggregate);
578                return FormatString (ids, TEXT("%s%s"), szServer, szAggregate);
579                }
580             break;
581
582          case alertSTOPPED:
583             ids = (fFull) ? IDS_ALERT_DESCFULL_STOPPED : IDS_ALERT_DESCSHORT_STOPPED;
584             lpiPrimary->GetServerName (szServer);
585             lpiPrimary->GetServiceName (szService);
586             return FormatString (ids, TEXT("%s%s%t%t%lu"), szServer, szService, &lpoa->aAlerts[ iAlertPrimary ].aiSTOPPED.stStopped, &lpoa->aAlerts[ iAlertPrimary ].aiSTOPPED.stLastError, lpoa->aAlerts[ iAlertPrimary ].aiSTOPPED.errLastError);
587
588          case alertBADCREDS:
589             ids = (fFull) ? IDS_ALERT_DESCFULL_BADCREDS : IDS_ALERT_DESCSHORT_BADCREDS;
590             lpiPrimary->GetServerName (szServer);
591             return FormatString (ids, TEXT("%s"), szServer);
592
593          case alertOVERALLOC:
594             lpiPrimary->GetServerName (szServer);
595             lpiPrimary->GetAggregateName (szAggregate);
596             ids = (fFull) ? IDS_ALERT_DESCFULL_AGG_ALLOC : IDS_ALERT_DESCSHORT_AGG_ALLOC;
597             return FormatString (ids, TEXT("%s%s%.1B%.1B"), szServer, szAggregate, 1024.0 * (double)(lpoa->aAlerts[ iAlertPrimary ].aiOVERALLOC.ckCapacity), 1024.0 * (double)(lpoa->aAlerts[ iAlertPrimary ].aiOVERALLOC.ckAllocated));
598
599          case alertSTATE_NO_VNODE:
600             ids = (fFull) ? IDS_ALERT_DESCFULL_STATE_NO_VNODE : IDS_ALERT_DESCSHORT_STATE_NO_VNODE;
601             lpiPrimary->GetServerName (szServer);
602             lpiPrimary->GetAggregateName (szAggregate);
603             lpiPrimary->GetFilesetName (szFileset);
604             return FormatString (ids, TEXT("%s%s%s%08lX"), szServer, szAggregate, szFileset, lpoa->aAlerts[ iAlertPrimary ].aiSTATE.State);
605
606          case alertSTATE_NO_SERVICE:
607             ids = (fFull) ? IDS_ALERT_DESCFULL_STATE_NO_SERVICE : IDS_ALERT_DESCSHORT_STATE_NO_SERVICE;
608             lpiPrimary->GetServerName (szServer);
609             lpiPrimary->GetAggregateName (szAggregate);
610             lpiPrimary->GetFilesetName (szFileset);
611             return FormatString (ids, TEXT("%s%s%s%08lX"), szServer, szAggregate, szFileset, lpoa->aAlerts[ iAlertPrimary ].aiSTATE.State);
612
613          case alertSTATE_OFFLINE:
614             ids = (fFull) ? IDS_ALERT_DESCFULL_STATE_OFFLINE : IDS_ALERT_DESCSHORT_STATE_OFFLINE;
615             lpiPrimary->GetServerName (szServer);
616             lpiPrimary->GetAggregateName (szAggregate);
617             lpiPrimary->GetFilesetName (szFileset);
618             return FormatString (ids, TEXT("%s%s%s%08lX"), szServer, szAggregate, szFileset, lpoa->aAlerts[ iAlertPrimary ].aiSTATE.State);
619          }
620       }
621
622    return NULL;
623 }
624
625
626 LPTSTR Alert_GetRemedyFunc (LPIDENT lpiPrimary, size_t iAlertPrimary, LPIDENT lpiServer)
627 {
628    LPOBJECTALERTS lpoa;
629    if ((lpoa = Alert_GetObjectAlerts (lpiPrimary)) != NULL)
630       {
631       switch (lpoa->aAlerts[ iAlertPrimary ].alert)
632          {
633          case alertTIMEOUT:
634             return FormatString (IDS_ALERT_FIX_TIMEOUT);
635          case alertFULL:
636             if (lpiPrimary->fIsAggregate())
637                return FormatString (IDS_ALERT_FIX_AGG_FULL);
638             else if (lpiPrimary->fIsFileset())
639                return FormatString (IDS_ALERT_FIX_SET_FULL);
640             break;
641          case alertNO_VLDBENT:
642             return FormatString (IDS_ALERT_FIX_NO_VLDBENT);
643          case alertNO_SVRENT:
644             if (lpiPrimary->fIsFileset())
645                return FormatString (IDS_ALERT_FIX_NO_SVRENT_SET);
646             else
647                return FormatString (IDS_ALERT_FIX_NO_SVRENT_AGG);
648             break;
649          case alertSTOPPED:
650             return FormatString (IDS_ALERT_FIX_STOPPED);
651          case alertBADCREDS:
652             return FormatString (IDS_ALERT_FIX_BADCREDS);
653          case alertOVERALLOC:
654             return FormatString (IDS_ALERT_FIX_AGG_ALLOC);
655          case alertSTATE_NO_VNODE:
656             return FormatString (IDS_ALERT_FIX_STATE_NO_VNODE);
657          case alertSTATE_NO_SERVICE:
658             return FormatString (IDS_ALERT_FIX_STATE_NO_SERVICE);
659          case alertSTATE_OFFLINE:
660             return FormatString (IDS_ALERT_FIX_STATE_OFFLINE);
661          }
662       }
663
664    return NULL;
665 }
666
667
668 LPTSTR Alert_GetButtonFunc (LPIDENT lpiPrimary, size_t iAlertPrimary, LPIDENT lpiServer)
669 {
670    LPOBJECTALERTS lpoa;
671    if ((lpoa = Alert_GetObjectAlerts (lpiPrimary)) != NULL)
672       {
673       switch (lpoa->aAlerts[ iAlertPrimary ].alert)
674          {
675          case alertTIMEOUT:
676             return FormatString (IDS_ALERT_BUTTON_TRYAGAIN);
677          case alertFULL:
678             return FormatString (IDS_ALERT_BUTTON_WARNINGS);
679          case alertNO_VLDBENT:
680             return NULL;
681          case alertNO_SVRENT:
682             return NULL;
683          case alertSTOPPED:
684             return FormatString (IDS_ALERT_BUTTON_VIEWLOG);
685          case alertBADCREDS:
686             return FormatString (IDS_ALERT_BUTTON_GETCREDS);
687          case alertOVERALLOC:
688             return NULL;
689          case alertSTATE_NO_VNODE:
690             return NULL;
691          case alertSTATE_NO_SERVICE:
692             return NULL;
693          case alertSTATE_OFFLINE:
694             return NULL;
695          }
696       }
697
698    return NULL;
699 }
700
701
702 /*
703  * SCOUT ______________________________________________________________________
704  *
705  * (okay, well, our simulated Scout anyway)
706  *
707  */
708
709 static HANDLE hScout = 0;       // scout's thread
710 static HANDLE heScoutWakeup = 0;        // scout's wakeup event
711
712 BOOL Alert_StartScout (ULONG *pStatus)
713 {
714    if (hScout == 0)  // create scout?
715       {
716       heScoutWakeup = CreateEvent (NULL, FALSE, FALSE, NULL);
717
718       DWORD dwThreadID;
719       if ((hScout = CreateThread (NULL, 0,
720                                   (LPTHREAD_START_ROUTINE)Alert_ScoutProc,
721                                   NULL, 0,
722                                   &dwThreadID)) == NULL)
723          {
724          if (pStatus)
725             *pStatus = GetLastError();
726          return FALSE;
727          }
728
729       SetThreadPriority (hScout, THREAD_PRIORITY_BELOW_NORMAL);
730       }
731    else // or just wake up scout from its slumber?
732       {
733       PulseEvent (heScoutWakeup);
734       }
735
736    return TRUE;
737 }
738
739
740 BOOL Alert_Scout_QueueCheckServer (LPIDENT lpiServer, ULONG *pStatus)
741 {
742    Alert_Scout_SetOutOfDate (lpiServer);
743    return Alert_StartScout (pStatus);
744 }
745
746
747 DWORD WINAPI Alert_ScoutProc (LPVOID lp)
748 {
749    // We'll keep working forever...
750    //
751    for (;;)
752       {
753       AfsClass_Enter();
754
755       LPCELL lpCell = (g.lpiCell == NULL) ? NULL : g.lpiCell->OpenCell();
756       if (lpCell != NULL)
757          {
758          // See if our credentials have expired
759          //
760          CheckForExpiredCredentials();
761
762          // See if any new servers have arrived, or old servers disappeared.
763          //
764          lpCell->RefreshServerList();
765
766          // Check all the out-of-date servers we can find.
767          //
768          HENUM hEnum;
769          for (LPSERVER lpServer = lpCell->ServerFindFirst (&hEnum); lpServer; lpServer = lpCell->ServerFindNext (&hEnum))
770             {
771             LPIDENT lpiServer = lpServer->GetIdentifier();
772             LPOBJECTALERTS lpoa;
773
774             if ( ((lpoa = Alert_GetObjectAlerts (lpiServer)) != NULL) &&
775                  (lpoa->dwTickNextTest <= GetTickCount()) )
776                {
777
778                // Okay!  We've found a server that needs to be tested for
779                // alert conditions.  Do that now, and when we're done, set
780                // its next query-time to some distance in the future.
781                //
782                if (lpoa->dwTickNextRefresh == 0)
783                   {
784                   if (lpoa->cTickRefresh != 0)
785                      lpoa->dwTickNextRefresh = lpoa->cTickRefresh + GetTickCount();
786                   }
787                else if (lpoa->dwTickNextRefresh <= GetTickCount())
788                   {
789                   (void)lpServer->Invalidate();
790                   (void)lpServer->RefreshAll();
791                   lpoa->dwTickNextRefresh = lpoa->cTickRefresh + GetTickCount();
792                   }
793
794                (void)Alert_Scout_CheckServer (lpServer);
795                }
796
797             lpServer->Close();
798             }
799
800          lpCell->Close();
801          }
802
803       AfsClass_Leave();
804
805       // Now that we have completed a pass over the servers in this cell,
806       // and now that we're not holding any critical sections on which
807       // other threads would otherwise block, go to sleep for a while.
808       //
809       WaitForSingleObjectEx (heScoutWakeup, 45L * cmsec1SECOND, FALSE);
810       }
811
812    return 0;
813 }
814
815
816 void Alert_Scout_SetOutOfDate (LPIDENT lpi)
817 {
818    LPOBJECTALERTS lpoa;
819    if ((lpoa = Alert_GetObjectAlerts (lpi, TRUE)) != NULL)
820       {
821       lpoa->dwTickNextTest = GetTickCount() -1;
822       }
823 }
824
825
826 void Alert_Scout_SetUpToDate (LPOBJECTALERTS lpoa)
827 {
828    if (lpoa != NULL)
829       {
830       lpoa->dwTickNextTest = GetTickCount() + lpoa->cTickRefresh;
831       }
832 }
833
834
835 BOOL Alert_Scout_CheckServer (LPSERVER lpServer)
836 {
837    BOOL rc = TRUE;
838
839    LPSERVER_PREF lpsp;
840    if ((lpsp = (LPSERVER_PREF)lpServer->GetUserParam()) != NULL)
841       {
842       LPOBJECTALERTS lpoa;
843       if ((lpoa = Alert_GetObjectAlerts (lpServer->GetIdentifier())) != NULL)
844          {
845          PostNotification (evtScoutBegin, lpServer->GetIdentifier());
846
847          BOOL fChangedServerAlerts = FALSE;
848
849          DWORD dwTickNextTestWhenStarted = lpoa->dwTickNextTest;
850
851          // First look through the server's aggregates and filesets, to
852          // find any which have usages over their warning threshholds.
853          //
854          HENUM heAggregate;
855          for (LPAGGREGATE lpAggregate = lpServer->AggregateFindFirst (&heAggregate); lpAggregate; lpAggregate = lpServer->AggregateFindNext (&heAggregate))
856             {
857             BOOL fChangedAggregateAlerts = FALSE;
858             LPIDENT lpiAggregate = lpAggregate->GetIdentifier();
859
860             LPOBJECTALERTS lpoaAggregate;
861             if ((lpoaAggregate = Alert_GetObjectAlerts (lpAggregate->GetIdentifier())) != NULL)
862                {
863                for (size_t iAlert = 0; iAlert < lpoaAggregate->nAlerts; )
864                   {
865                   if ( (lpoaAggregate->aAlerts[ iAlert ].alert == alertFULL) ||
866                        (lpoaAggregate->aAlerts[ iAlert ].alert == alertOVERALLOC) ||
867                        (lpoaAggregate->aAlerts[ iAlert ].alert == alertNO_SVRENT) )
868                      {
869                      fChangedAggregateAlerts = TRUE;
870                      fChangedServerAlerts = TRUE;
871                      Alert_Remove (lpAggregate->GetIdentifier(), iAlert);
872                      }
873                   else
874                      ++iAlert;
875                   }
876
877                LPAGGREGATE_PREF lpap;
878                if ((lpap = (LPAGGREGATE_PREF)lpAggregate->GetUserParam()) != NULL)
879                   {
880                   short wGhost = lpAggregate->GetGhostStatus();
881                   if (lpsp->fWarnAggNoServ && !(wGhost & GHOST_HAS_SERVER_ENTRY))
882                      {
883                      ALERTINFO ai;
884                      ai.alert = alertNO_SVRENT;
885                      Alert_AddPrimary (lpAggregate->GetIdentifier(), &ai);
886                      fChangedAggregateAlerts = TRUE;
887                      fChangedServerAlerts = TRUE;
888                      }
889
890                   if (lpsp->fWarnAggAlloc && lpap->fWarnAggAlloc)
891                      {
892                      AGGREGATESTATUS as;
893                      if (lpAggregate->GetStatus (&as, TRUE))
894                         {
895                         if (as.ckStorageAllocated > as.ckStorageTotal)
896                            {
897                            ALERTINFO ai;
898                            ai.alert = alertOVERALLOC;
899                            ai.aiOVERALLOC.ckAllocated = as.ckStorageAllocated;
900                            ai.aiOVERALLOC.ckCapacity = as.ckStorageTotal;
901                            Alert_AddPrimary (lpAggregate->GetIdentifier(), &ai);
902                            fChangedAggregateAlerts = TRUE;
903                            fChangedServerAlerts = TRUE;
904                            }
905                         }
906                      }
907
908                   short perWarnAggFull = lpap->perWarnAggFull;
909                   if (perWarnAggFull == -1)
910                      perWarnAggFull = lpsp->perWarnAggFull;
911                   if (perWarnAggFull != 0)
912                      {
913                      AGGREGATESTATUS as;
914                      if (lpAggregate->GetStatus (&as, TRUE))
915                         {
916                         if (as.ckStorageTotal != 0)
917                            {
918                            short perNow = (short)( (double)(as.ckStorageTotal - as.ckStorageFree) * 100.0 / (double)(as.ckStorageTotal) );
919
920                            if (perNow > perWarnAggFull)
921                               {
922                               ALERTINFO ai;
923                               ai.alert = alertFULL;
924                               ai.aiFULL.perWarning = perWarnAggFull;
925                               ai.aiFULL.ckWarning = (ULONG)( (double)perWarnAggFull * (double)(as.ckStorageTotal) / 100.0 );
926                               Alert_AddPrimary (lpAggregate->GetIdentifier(), &ai);
927                               fChangedAggregateAlerts = TRUE;
928                               fChangedServerAlerts = TRUE;
929                               }
930                            }
931                         }
932                      }
933                   }
934                }
935
936             HENUM heFileset;
937             for (LPFILESET lpFileset = lpAggregate->FilesetFindFirst (&heFileset); lpFileset; lpFileset = lpAggregate->FilesetFindNext (&heFileset))
938                {
939                BOOL fChangedFilesetAlerts = FALSE;
940                LPIDENT lpiFileset = lpFileset->GetIdentifier();
941
942                LPOBJECTALERTS lpoaFileset;
943                if ((lpoaFileset = Alert_GetObjectAlerts (lpFileset->GetIdentifier())) != NULL)
944                   {
945                   for (size_t iAlert = 0; iAlert < lpoaFileset->nAlerts; )
946                      {
947                      if ( (lpoaFileset->aAlerts[ iAlert ].alert == alertFULL) ||
948                           (lpoaFileset->aAlerts[ iAlert ].alert == alertSTATE_NO_VNODE) ||
949                           (lpoaFileset->aAlerts[ iAlert ].alert == alertSTATE_NO_SERVICE) ||
950                           (lpoaFileset->aAlerts[ iAlert ].alert == alertSTATE_OFFLINE) ||
951                           (lpoaFileset->aAlerts[ iAlert ].alert == alertNO_VLDBENT) ||
952                           (lpoaFileset->aAlerts[ iAlert ].alert == alertNO_SVRENT) )
953                         {
954                         fChangedFilesetAlerts = TRUE;
955                         fChangedServerAlerts = TRUE;
956                         Alert_Remove (lpFileset->GetIdentifier(), iAlert);
957                         }
958                      else
959                         ++iAlert;
960                      }
961                   }
962
963                LPFILESET_PREF lpfp;
964                if ((lpfp = (LPFILESET_PREF)lpFileset->GetUserParam()) != NULL)
965                   {
966                   FILESETSTATUS fs;
967                   if (lpFileset->GetStatus (&fs, TRUE))
968                      {
969                      if (fs.State & fsNO_VNODE)
970                         {
971                         ALERTINFO ai;
972                         ai.alert = alertSTATE_NO_VNODE;
973                         ai.aiSTATE.State = fs.State;
974                         Alert_AddPrimary (lpFileset->GetIdentifier(), &ai);
975                         fChangedFilesetAlerts = TRUE;
976                         fChangedServerAlerts = TRUE;
977                         }
978                      else if (fs.State & fsNO_SERVICE)
979                         {
980                         ALERTINFO ai;
981                         ai.alert = alertSTATE_NO_SERVICE;
982                         ai.aiSTATE.State = fs.State;
983                         Alert_AddPrimary (lpFileset->GetIdentifier(), &ai);
984                         fChangedFilesetAlerts = TRUE;
985                         fChangedServerAlerts = TRUE;
986                         }
987                      else if (fs.State & fsOFFLINE)
988                         {
989                         ALERTINFO ai;
990                         ai.alert = alertSTATE_OFFLINE;
991                         ai.aiSTATE.State = fs.State;
992                         Alert_AddPrimary (lpFileset->GetIdentifier(), &ai);
993                         fChangedFilesetAlerts = TRUE;
994                         fChangedServerAlerts = TRUE;
995                         }
996
997                      short perWarnSetFull = lpfp->perWarnSetFull;
998                      if (perWarnSetFull == -1)
999                         perWarnSetFull = lpsp->perWarnSetFull;
1000                      if (perWarnSetFull != 0)
1001                         {
1002                         if (fs.Type == ftREADWRITE)
1003                            {
1004                            if (fs.ckQuota != 0)
1005                               {
1006                               short perNow = (short)( (double)(fs.ckUsed) * 100.0 / (double)(fs.ckQuota) );
1007
1008                               if (perNow > perWarnSetFull)
1009                                  {
1010                                  ALERTINFO ai;
1011                                  ai.alert = alertFULL;
1012                                  ai.aiFULL.perWarning = perWarnSetFull;
1013                                  ai.aiFULL.ckWarning = (ULONG)( (double)perWarnSetFull * (double)(fs.ckQuota) / 100.0 );
1014                                  Alert_AddPrimary (lpFileset->GetIdentifier(), &ai);
1015                                  fChangedFilesetAlerts = TRUE;
1016                                  fChangedServerAlerts = TRUE;
1017                                  }
1018                               }
1019                            }
1020                         }
1021                      }
1022
1023                   short wGhost = lpFileset->GetGhostStatus();
1024                   if (lpsp->fWarnSetNoVLDB && !(wGhost & GHOST_HAS_VLDB_ENTRY))
1025                      {
1026                      ALERTINFO ai;
1027                      ai.alert = alertNO_VLDBENT;
1028                      Alert_AddPrimary (lpFileset->GetIdentifier(), &ai);
1029                      fChangedFilesetAlerts = TRUE;
1030                      fChangedServerAlerts = TRUE;
1031                      }
1032                   if (lpsp->fWarnSetNoServ && !(wGhost & GHOST_HAS_SERVER_ENTRY) && !(fs.Type == ftREPLICA))
1033                      {
1034                      ALERTINFO ai;
1035                      ai.alert = alertNO_SVRENT;
1036                      Alert_AddPrimary (lpFileset->GetIdentifier(), &ai);
1037                      fChangedFilesetAlerts = TRUE;
1038                      fChangedServerAlerts = TRUE;
1039                      }
1040                   }
1041
1042                lpFileset->Close();
1043                if (fChangedFilesetAlerts)
1044                   {
1045                   PostNotification (evtAlertsChanged, lpiFileset);
1046                   }
1047                }
1048
1049             lpAggregate->Close();
1050             if (fChangedAggregateAlerts)
1051                {
1052                PostNotification (evtAlertsChanged, lpiAggregate);
1053                }
1054             }
1055
1056          // Next look through the server's servces to find any
1057          // which have stopped.
1058          //
1059          HENUM heService;
1060          for (LPSERVICE lpService = lpServer->ServiceFindFirst (&heService); lpService; lpService = lpServer->ServiceFindNext (&heService))
1061             {
1062             BOOL fChangedServiceAlerts = FALSE;
1063             LPIDENT lpiService = lpService->GetIdentifier();
1064
1065             LPOBJECTALERTS lpoaService;
1066             if ((lpoaService = Alert_GetObjectAlerts (lpService->GetIdentifier())) != NULL)
1067                {
1068                for (size_t iAlert = 0; iAlert < lpoaService->nAlerts; )
1069                   {
1070                   if (lpoaService->aAlerts[ iAlert ].alert == alertSTOPPED)
1071                      {
1072                      fChangedServiceAlerts = TRUE;
1073                      fChangedServerAlerts = TRUE;
1074                      Alert_Remove (lpService->GetIdentifier(), iAlert);
1075                      }
1076                   else
1077                      ++iAlert;
1078                   }
1079
1080                LPSERVICE_PREF lpcp;
1081                if ((lpcp = (LPSERVICE_PREF)lpService->GetUserParam()) != NULL)
1082                   {
1083                   if (lpcp->fWarnSvcStop && lpsp->fWarnSvcStop)
1084                      {
1085                      SERVICESTATUS ss;
1086                      if (lpService->GetStatus (&ss, TRUE))
1087                         {
1088                         if (ss.state != SERVICESTATE_RUNNING)
1089                            {
1090                            ALERTINFO ai;
1091                            ai.alert = alertSTOPPED;
1092                            memcpy (&ai.aiSTOPPED.stStopped,   &ss.timeLastStop, sizeof(SYSTEMTIME));
1093                            memcpy (&ai.aiSTOPPED.stLastError, &ss.timeLastFail, sizeof(SYSTEMTIME));
1094                            ai.aiSTOPPED.errLastError = ss.dwErrLast;
1095                            Alert_AddPrimary (lpService->GetIdentifier(), &ai);
1096                            fChangedServiceAlerts = TRUE;
1097                            fChangedServerAlerts = TRUE;
1098                            }
1099                         }
1100                      }
1101                   }
1102                }
1103
1104             lpService->Close();
1105             if (fChangedServiceAlerts)
1106                {
1107                PostNotification (evtAlertsChanged, lpiService);
1108                }
1109             }
1110
1111          if (rc && (dwTickNextTestWhenStarted == lpoa->dwTickNextTest))
1112             {
1113             Alert_Scout_SetUpToDate (lpoa);
1114             }
1115
1116          if (fChangedServerAlerts)
1117             {
1118             PostNotification (evtAlertsChanged, lpServer->GetIdentifier());
1119             }
1120
1121          PostNotification (evtScoutEnd, lpServer->GetIdentifier());
1122          }
1123       }
1124
1125    return rc;
1126 }
1127