4 // Routines to handle flushing AFS volumes in response to
5 // System Power event notification such as Hibernate request.
7 /////////////////////////////////////////////////////////////////////
17 #include "afsd_init.h"
28 #include "afsd_flushvol.h"
29 #include "afsd_eventlog.h"
30 #include "lanahelper.h"
32 extern void afsi_log(char *pattern, ...);
34 static FLUSHVOLTHREADINFO gThreadInfo = {0};
35 static HANDLE gThreadHandle = NULL;
38 /////////////////////////////////////////////////////////////////////
40 // Call routine found in FS.EXE to flush volume.
42 // At entry, input param is UNC string for volume,
43 // e.g. '\\afs\all\athena.mit.edu\user\m\h\mholiday'
45 // I believe that success from 'pioctl' routine
46 // indicated by return value of zero (0).
49 afsd_ServicePerformFlushVolumeCmd(char *data)
51 register afs_int32 code;
52 struct ViceIoctl blob;
54 afsi_log("Flushing Volume \"%s\"",data);
55 memset(&blob, '\0', sizeof(blob));
56 code = pioctl(data, VIOC_FLUSHVOLUME, &blob, 0);
62 afsd_ServicePerformFlushVolumes()
64 CONST CHAR COLON = ':';
65 CONST CHAR SLASH = '\\';
66 CONST DWORD NETRESBUFSIZE = 16384;
67 CHAR bufMessage[1024];
72 DWORD dwNetResBufSize;
73 DWORD dwTotalVols = 0;
74 DWORD dwVolBegin, dwVolEnd;
75 DWORD dwFlushBegin, dwFlushEnd;
77 LPNETRESOURCE lpNetResBuf, lpnr;
78 PCHAR pszShareName, pc;
81 if ( lana_OnlyLoopback() ) {
82 // Nothing to do if we only have a loopback interface
86 // Determine the root share name (\\AFS\ALL or \\<machine>-AFS\ALL),
87 // and the length of the server name prefix.
88 pszShareName = smb_GetSharename();
89 if (pszShareName == NULL)
91 LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_NO_SHARE_NAME, NULL);
94 pc = strrchr(pszShareName, SLASH);
95 if ((pc == NULL) || ((dwServerSize = pc - pszShareName) < 3))
97 LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_BAD_SHARE_NAME,
103 // Allocate a buffer to hold network resources returned by
104 // WNetEnumResource().
105 lpNetResBuf = malloc(NETRESBUFSIZE);
106 if (lpNetResBuf == NULL)
108 // Out of memory, give up now.
109 LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_NO_MEMORY, NULL);
114 // Initialize the flush timer. Note that GetTickCount() returns
115 // the number of milliseconds since the system started, in a DWORD,
116 // so that the value wraps around every 49.7 days. We do not bother
117 // to handle the case where the flush elapsed time is greater than
119 dwFlushBegin = GetTickCount();
121 dwRet = WNetOpenEnum(RESOURCE_CONNECTED, RESOURCETYPE_ANY, 0, NULL,
123 if (dwRet != NO_ERROR)
125 LogEventMessage(EVENTLOG_ERROR_TYPE, MSG_FLUSH_OPEN_ENUM_ERROR,
131 // Loop to enumerate network resources, and flush those associated
136 memset(lpNetResBuf, 0, NETRESBUFSIZE);
137 dwNetResBufSize = NETRESBUFSIZE;
138 dwRet = WNetEnumResource(hEnum, &dwCount,
139 lpNetResBuf, &dwNetResBufSize);
140 if (dwRet != NO_ERROR)
142 // Iterate over the returned network resources.
143 for (i = 0, lpnr = lpNetResBuf; i < dwCount; i++, lpnr++)
145 // Ensure resource has a remote name, and is connected.
146 if ((lpnr->lpRemoteName == NULL) ||
147 (lpnr->dwScope != RESOURCE_CONNECTED))
149 if ((_strnicmp(lpnr->lpRemoteName, pszShareName,
150 dwServerSize) == 0) &&
151 (lpnr->lpRemoteName[dwServerSize] == SLASH))
154 // but we don't want to flush '\\[...]afs\all'
155 if (_stricmp(lpnr->lpRemoteName, pszShareName) == 0)
159 dwVolBegin = GetTickCount();
160 afsRet = afsd_ServicePerformFlushVolumeCmd(lpnr->lpRemoteName);
161 dwVolEnd = GetTickCount();
164 LogTimingEvent(MSG_TIME_FLUSH_PER_VOLUME,
166 dwVolEnd - dwVolBegin);
170 LogEvent(EVENTLOG_WARNING_TYPE,
172 lpnr->lpRemoteName, NULL);
177 WNetCloseEnum(hEnum);
180 if (dwRet != ERROR_NO_MORE_ITEMS)
182 LogEventMessage(EVENTLOG_ERROR_TYPE, MSG_FLUSH_ENUM_ERROR,
187 dwFlushEnd = GetTickCount();
189 // display total volume count in Event Logger
190 sprintf(bufMessage, "%d", dwTotalVols);
191 LogTimingEvent(MSG_TIME_FLUSH_TOTAL, bufMessage,
192 dwFlushEnd - dwFlushBegin);
197 // Report a timing event to the system event log.
198 // The lpszString1 argument is the first substitution string for the
199 // given event ID. The time argument will be converted into the
200 // second substitution string.
202 LogTimingEvent(DWORD dwEventID, LPTSTR lpString1, DWORD dwTime)
206 sprintf(szTime, "%lu", dwTime);
207 LogEvent(EVENTLOG_INFORMATION_TYPE, dwEventID, lpString1, szTime,
212 /////////////////////////////////////////////////////////////////////
216 // Obtain token for the currently logged-in user.
218 // This routine looks for a window which we 'know' belongs to
219 // the shell, and from there we follow a route which leads to
220 // getting a handle on an access token owned by the shell.
222 // The return value is either a handle to a suitable token,
225 // One of the times that this function might return null
226 // is when there is no logged-in user. Other cases include
227 // insufficient access to the desktop, etc.
230 // Portions of this routine found in various newsgroups
232 HANDLE GetUserToken(DWORD access)
235 DWORD pid = 0, tid = 0;
237 // Try it the easy way first - look for a window owned by the shell on
238 // our current desktop. If we find one, use that to get the process id.
239 HWND shell = FindWindowEx(NULL, NULL, "Progman", NULL);
242 tid = GetWindowThreadProcessId(shell, &pid);
245 // We are possibly running on a private window station and desktop: we must
246 // switch to the default (which we suppose is where we will find the
250 HWINSTA saveWinSta = GetProcessWindowStation();
251 HDESK saveDesk = GetThreadDesktop(GetCurrentThreadId());
252 HWINSTA winSta = NULL;
254 BOOL changeFlag = FALSE;
255 BOOL dummy = saveWinSta != NULL &&
257 (winSta = OpenWindowStation("WinSta0", FALSE,
258 MAXIMUM_ALLOWED)) != NULL &&
259 (changeFlag = SetProcessWindowStation(winSta)) != 0 &&
260 (desk = OpenDesktop("Default", 0, FALSE,
261 MAXIMUM_ALLOWED)) != NULL &&
262 SetThreadDesktop(desk) != 0;
264 // Now find the window and process on this desktop
265 shell = FindWindowEx(NULL, NULL, "Progman", NULL);
268 tid = GetWindowThreadProcessId(shell, &pid);
271 // Restore our own window station and desktop
274 SetProcessWindowStation(saveWinSta);
275 SetThreadDesktop(saveDesk);
278 // Close temporary objects
280 CloseWindowStation(winSta);
286 // If we have a process id, use that to get the process handle and
287 // from there the process' access token.
291 HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
294 OpenProcessToken(hProc, access, &hTok) || (hTok = NULL);
299 // Return token if we got one
303 // impersonate logged-on user as client
307 DWORD dwDesiredAccess = TOKEN_ALL_ACCESS;
308 HANDLE hUserToken = GetUserToken(dwDesiredAccess);
310 if (hUserToken == NULL)
312 if (ImpersonateLoggedOnUser(hUserToken) == 0)
314 LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_IMPERSONATE_ERROR,
321 /////////////////////////////////////////////////////////////////////
326 afsd_ServiceFlushVolumesThreadProc(LPVOID lpParam)
328 FLUSHVOLTHREADINFO ThreadInfo;
329 PFLUSHVOLTHREADINFO pThreadInfo = (PFLUSHVOLTHREADINFO) lpParam;
330 HANDLE arHandles[2] = {0};
331 DWORD dwWaitState = 0;
333 // thread running - get handles
334 ThreadInfo.hEventPowerEvent = pThreadInfo->hEventPowerEvent;
335 ThreadInfo.hEventResumeMain = pThreadInfo->hEventResumeMain;
336 ThreadInfo.hEventTerminate = pThreadInfo->hEventTerminate;
339 arHandles[0] = ThreadInfo.hEventTerminate;
340 arHandles[1] = ThreadInfo.hEventPowerEvent;
345 // wait for an event to happen
346 dwWaitState = WaitForMultipleObjectsEx(2, arHandles, FALSE, INFINITE, FALSE);
351 // termination signaled
353 CheckAndCloseHandle(ThreadInfo.hEventPowerEvent);
354 CheckAndCloseHandle(ThreadInfo.hEventResumeMain);
355 CheckAndCloseHandle(ThreadInfo.hEventTerminate);
359 case WAIT_OBJECT_0+1:
362 if (ImpersonateClient())
364 afsd_ServicePerformFlushVolumes();
367 ResetEvent(ThreadInfo.hEventPowerEvent);
370 case WAIT_ABANDONED_0:
371 case WAIT_ABANDONED_0+1:
372 case WAIT_IO_COMPLETION:
375 LogEvent(EVENTLOG_WARNING_TYPE,
376 MSG_FLUSH_UNEXPECTED_EVENT, NULL);
381 // signal back to waiting mainline
382 SetEvent(ThreadInfo.hEventResumeMain);
386 // I suppose we never get here
390 /////////////////////////////////////////////////////////////////////
392 // Mainline thread routines
396 CheckAndCloseHandle(HANDLE thisHandle)
398 if (thisHandle != NULL)
400 CloseHandle(thisHandle);
409 PowerNotificationThreadCreate()
411 BOOL bSuccess = FALSE;
412 DWORD dwThreadId = 0;
413 char eventName[MAX_PATH];
417 // create power event notification event
418 // bManualReset=TRUE, bInitialState=FALSE
419 gThreadInfo.hEventPowerEvent = CreateEvent(NULL, TRUE, FALSE,
420 TEXT("afsd_flushvol_EventPowerEvent"));
421 if ( GetLastError() == ERROR_ALREADY_EXISTS )
422 afsi_log("Event Object Already Exists: %s", eventName);
423 if (gThreadInfo.hEventPowerEvent == NULL)
426 // create mainline resume event
427 // bManualReset=FALSE, bInitialState=FALSE
428 gThreadInfo.hEventResumeMain = CreateEvent(NULL, FALSE, FALSE,
429 TEXT("afsd_flushvol_EventResumeMain"));
430 if ( GetLastError() == ERROR_ALREADY_EXISTS )
431 afsi_log("Event Object Already Exists: %s", eventName);
432 if (gThreadInfo.hEventResumeMain == NULL)
435 // create thread terminate event
436 // bManualReset=FALSE, bInitialState=FALSE
437 gThreadInfo.hEventTerminate = CreateEvent(NULL, FALSE, FALSE,
438 TEXT("afsd_flushvol_EventTerminate"));
439 if ( GetLastError() == ERROR_ALREADY_EXISTS )
440 afsi_log("Event Object Already Exists: %s", eventName);
441 if (gThreadInfo.hEventTerminate == NULL)
444 // good so far - create thread
445 gThreadHandle = CreateThread(NULL, 0,
446 afsd_ServiceFlushVolumesThreadProc,
447 (LPVOID) &gThreadInfo,
460 CheckAndCloseHandle(gThreadInfo.hEventPowerEvent);
461 CheckAndCloseHandle(gThreadInfo.hEventResumeMain);
462 CheckAndCloseHandle(gThreadInfo.hEventTerminate);
463 CheckAndCloseHandle(gThreadHandle);
470 // Thread Notification
473 PowerNotificationThreadNotify()
478 // Notify thread of power event, and wait for the HardDead timeout period
479 dwRet = SignalObjectAndWait(
480 gThreadInfo.hEventPowerEvent, // object to signal
481 gThreadInfo.hEventResumeMain, // object to watch
482 HardDeadtimeout*1000, // timeout (ms)
486 if (dwRet == WAIT_OBJECT_0)
493 // Thread Termination
496 PowerNotificationThreadExit()
501 SetEvent(gThreadInfo.hEventTerminate);
502 WaitForSingleObject(gThreadHandle, INFINITE);
503 CloseHandle(gThreadHandle);