4 // Routines to handle flushing AFS volumes in response to
5 // System Power event notification such as Hibernate request.
7 /////////////////////////////////////////////////////////////////////
10 #include <afs/param.h>
20 #include "afsd_init.h"
31 #include "afsd_flushvol.h"
32 #include "afsd_eventlog.h"
33 #include "lanahelper.h"
35 extern void afsi_log(char *pattern, ...);
37 static FLUSHVOLTHREADINFO gThreadInfo = {0};
38 static HANDLE gThreadHandle = NULL;
41 /////////////////////////////////////////////////////////////////////
43 // Call routine found in FS.EXE to flush volume.
45 // At entry, input param is UNC string for volume,
46 // e.g. '\\afs\all\athena.mit.edu\user\m\h\mholiday'
48 // I believe that success from 'pioctl' routine
49 // indicated by return value of zero (0).
52 afsd_ServicePerformFlushVolumeCmd(char *data)
55 struct ViceIoctl blob;
57 afsi_log("Flushing Volume \"%s\"",data);
58 memset(&blob, '\0', sizeof(blob));
59 code = pioctl(data, VIOC_FLUSHVOLUME, &blob, 0);
65 afsd_ServicePerformFlushVolumes()
67 CONST CHAR COLON = ':';
68 CONST CHAR SLASH = '\\';
69 CONST DWORD NETRESBUFSIZE = 16384;
70 CHAR bufMessage[1024];
75 DWORD dwNetResBufSize;
76 DWORD dwTotalVols = 0;
77 DWORD dwVolBegin, dwVolEnd;
78 DWORD dwFlushBegin, dwFlushEnd;
80 LPNETRESOURCE lpNetResBuf, lpnr;
81 char *pszShareName, *pc;
84 if ( lana_OnlyLoopback() ) {
85 // Nothing to do if we only have a loopback interface
89 // Determine the root share name (\\AFS\ALL or \\<machine>-AFS\ALL),
90 // and the length of the server name prefix.
91 pszShareName = smb_GetSharename();
92 if (pszShareName == NULL)
94 LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_NO_SHARE_NAME, NULL);
97 pc = strrchr(pszShareName, SLASH);
98 if ((pc == NULL) || ((dwServerSize = (DWORD)(pc - pszShareName)) < 3))
100 LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_BAD_SHARE_NAME,
106 // Allocate a buffer to hold network resources returned by
107 // WNetEnumResource().
108 lpNetResBuf = malloc(NETRESBUFSIZE);
109 if (lpNetResBuf == NULL)
111 // Out of memory, give up now.
112 LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_NO_MEMORY, NULL);
117 // Initialize the flush timer. Note that GetTickCount() returns
118 // the number of milliseconds since the system started, in a DWORD,
119 // so that the value wraps around every 49.7 days. We do not bother
120 // to handle the case where the flush elapsed time is greater than
122 dwFlushBegin = GetTickCount();
124 dwRet = WNetOpenEnum(RESOURCE_CONNECTED, RESOURCETYPE_ANY, 0, NULL,
126 if (dwRet != NO_ERROR)
128 LogEventMessage(EVENTLOG_ERROR_TYPE, MSG_FLUSH_OPEN_ENUM_ERROR,
134 // Loop to enumerate network resources, and flush those associated
139 memset(lpNetResBuf, 0, NETRESBUFSIZE);
140 dwNetResBufSize = NETRESBUFSIZE;
141 dwRet = WNetEnumResource(hEnum, &dwCount,
142 lpNetResBuf, &dwNetResBufSize);
143 if (dwRet != NO_ERROR)
145 // Iterate over the returned network resources.
146 for (i = 0, lpnr = lpNetResBuf; i < dwCount; i++, lpnr++)
148 // Ensure resource has a remote name, and is connected.
149 if ((lpnr->lpRemoteName == NULL) ||
150 (lpnr->dwScope != RESOURCE_CONNECTED))
152 if ((_strnicmp(lpnr->lpRemoteName, pszShareName,
153 dwServerSize) == 0) &&
154 (lpnr->lpRemoteName[dwServerSize] == SLASH))
157 // but we don't want to flush '\\[...]afs\all'
158 if (cm_stricmp_utf8(lpnr->lpRemoteName, pszShareName) == 0)
162 dwVolBegin = GetTickCount();
163 afsRet = afsd_ServicePerformFlushVolumeCmd(lpnr->lpRemoteName);
164 dwVolEnd = GetTickCount();
167 LogTimingEvent(MSG_TIME_FLUSH_PER_VOLUME,
169 dwVolEnd - dwVolBegin);
173 LogEvent(EVENTLOG_WARNING_TYPE,
175 lpnr->lpRemoteName, NULL);
180 WNetCloseEnum(hEnum);
183 if (dwRet != ERROR_NO_MORE_ITEMS)
185 LogEventMessage(EVENTLOG_ERROR_TYPE, MSG_FLUSH_ENUM_ERROR,
190 dwFlushEnd = GetTickCount();
192 // display total volume count in Event Logger
193 sprintf(bufMessage, "%d", dwTotalVols);
194 LogTimingEvent(MSG_TIME_FLUSH_TOTAL, bufMessage,
195 dwFlushEnd - dwFlushBegin);
200 // Report a timing event to the system event log.
201 // The lpszString1 argument is the first substitution string for the
202 // given event ID. The time argument will be converted into the
203 // second substitution string.
205 LogTimingEvent(DWORD dwEventID, LPTSTR lpString1, DWORD dwTime)
209 sprintf(szTime, "%lu", dwTime);
210 LogEvent(EVENTLOG_INFORMATION_TYPE, dwEventID, lpString1, szTime,
215 /////////////////////////////////////////////////////////////////////
219 // Obtain token for the currently logged-in user.
221 // This routine looks for a window which we 'know' belongs to
222 // the shell, and from there we follow a route which leads to
223 // getting a handle on an access token owned by the shell.
225 // The return value is either a handle to a suitable token,
228 // One of the times that this function might return null
229 // is when there is no logged-in user. Other cases include
230 // insufficient access to the desktop, etc.
233 // Portions of this routine found in various newsgroups
235 HANDLE GetUserToken(DWORD access)
238 DWORD pid = 0, tid = 0;
240 // Try it the easy way first - look for a window owned by the shell on
241 // our current desktop. If we find one, use that to get the process id.
242 HWND shell = FindWindowEx(NULL, NULL, "Progman", NULL);
245 tid = GetWindowThreadProcessId(shell, &pid);
248 // We are possibly running on a private window station and desktop: we must
249 // switch to the default (which we suppose is where we will find the
253 HWINSTA saveWinSta = GetProcessWindowStation();
254 HDESK saveDesk = GetThreadDesktop(GetCurrentThreadId());
255 HWINSTA winSta = NULL;
257 BOOL changeFlag = FALSE;
258 BOOL dummy = saveWinSta != NULL &&
260 (winSta = OpenWindowStation("WinSta0", FALSE,
261 MAXIMUM_ALLOWED)) != NULL &&
262 (changeFlag = SetProcessWindowStation(winSta)) != 0 &&
263 (desk = OpenDesktop("Default", 0, FALSE,
264 MAXIMUM_ALLOWED)) != NULL &&
265 SetThreadDesktop(desk) != 0;
267 // Now find the window and process on this desktop
268 shell = FindWindowEx(NULL, NULL, "Progman", NULL);
271 tid = GetWindowThreadProcessId(shell, &pid);
274 // Restore our own window station and desktop
277 SetProcessWindowStation(saveWinSta);
278 SetThreadDesktop(saveDesk);
281 // Close temporary objects
283 CloseWindowStation(winSta);
289 // If we have a process id, use that to get the process handle and
290 // from there the process' access token.
294 HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
297 OpenProcessToken(hProc, access, &hTok) || (hTok = NULL);
302 // Return token if we got one
306 // impersonate logged-on user as client
310 DWORD dwDesiredAccess = TOKEN_ALL_ACCESS;
311 HANDLE hUserToken = GetUserToken(dwDesiredAccess);
313 if (hUserToken == NULL)
315 if (ImpersonateLoggedOnUser(hUserToken) == 0)
317 LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_IMPERSONATE_ERROR,
324 /////////////////////////////////////////////////////////////////////
329 afsd_ServiceFlushVolumesThreadProc(LPVOID lpParam)
331 FLUSHVOLTHREADINFO ThreadInfo;
332 PFLUSHVOLTHREADINFO pThreadInfo = (PFLUSHVOLTHREADINFO) lpParam;
333 HANDLE arHandles[2] = {0};
334 DWORD dwWaitState = 0;
336 // thread running - get handles
337 ThreadInfo.hEventPowerEvent = pThreadInfo->hEventPowerEvent;
338 ThreadInfo.hEventResumeMain = pThreadInfo->hEventResumeMain;
339 ThreadInfo.hEventTerminate = pThreadInfo->hEventTerminate;
342 arHandles[0] = ThreadInfo.hEventTerminate;
343 arHandles[1] = ThreadInfo.hEventPowerEvent;
348 // wait for an event to happen
349 dwWaitState = WaitForMultipleObjectsEx(2, arHandles, FALSE, INFINITE, FALSE);
354 // termination signaled
356 CheckAndCloseHandle(ThreadInfo.hEventPowerEvent);
357 CheckAndCloseHandle(ThreadInfo.hEventResumeMain);
358 CheckAndCloseHandle(ThreadInfo.hEventTerminate);
362 case WAIT_OBJECT_0+1:
365 if (ImpersonateClient())
367 afsd_ServicePerformFlushVolumes();
370 ResetEvent(ThreadInfo.hEventPowerEvent);
373 case WAIT_ABANDONED_0:
374 case WAIT_ABANDONED_0+1:
375 case WAIT_IO_COMPLETION:
378 LogEvent(EVENTLOG_WARNING_TYPE,
379 MSG_FLUSH_UNEXPECTED_EVENT, NULL);
384 // signal back to waiting mainline
385 SetEvent(ThreadInfo.hEventResumeMain);
389 // I suppose we never get here
393 /////////////////////////////////////////////////////////////////////
395 // Mainline thread routines
399 CheckAndCloseHandle(HANDLE thisHandle)
401 if (thisHandle != NULL)
403 CloseHandle(thisHandle);
412 PowerNotificationThreadCreate()
414 BOOL bSuccess = FALSE;
415 DWORD dwThreadId = 0;
416 char eventName[MAX_PATH];
420 // create power event notification event
421 // bManualReset=TRUE, bInitialState=FALSE
422 gThreadInfo.hEventPowerEvent = CreateEvent(NULL, TRUE, FALSE,
423 TEXT("afsd_flushvol_EventPowerEvent"));
424 if ( GetLastError() == ERROR_ALREADY_EXISTS )
425 afsi_log("Event Object Already Exists: %s", eventName);
426 if (gThreadInfo.hEventPowerEvent == NULL)
429 // create mainline resume event
430 // bManualReset=FALSE, bInitialState=FALSE
431 gThreadInfo.hEventResumeMain = CreateEvent(NULL, FALSE, FALSE,
432 TEXT("afsd_flushvol_EventResumeMain"));
433 if ( GetLastError() == ERROR_ALREADY_EXISTS )
434 afsi_log("Event Object Already Exists: %s", eventName);
435 if (gThreadInfo.hEventResumeMain == NULL)
438 // create thread terminate event
439 // bManualReset=FALSE, bInitialState=FALSE
440 gThreadInfo.hEventTerminate = CreateEvent(NULL, FALSE, FALSE,
441 TEXT("afsd_flushvol_EventTerminate"));
442 if ( GetLastError() == ERROR_ALREADY_EXISTS )
443 afsi_log("Event Object Already Exists: %s", eventName);
444 if (gThreadInfo.hEventTerminate == NULL)
447 // good so far - create thread
448 gThreadHandle = CreateThread(NULL, 0,
449 afsd_ServiceFlushVolumesThreadProc,
450 (LPVOID) &gThreadInfo,
463 CheckAndCloseHandle(gThreadInfo.hEventPowerEvent);
464 CheckAndCloseHandle(gThreadInfo.hEventResumeMain);
465 CheckAndCloseHandle(gThreadInfo.hEventTerminate);
466 CheckAndCloseHandle(gThreadHandle);
473 // Thread Notification
476 PowerNotificationThreadNotify()
481 // Notify thread of power event, and wait for the HardDead timeout period
482 dwRet = SignalObjectAndWait(
483 gThreadInfo.hEventPowerEvent, // object to signal
484 gThreadInfo.hEventResumeMain, // object to watch
485 HardDeadtimeout*1000, // timeout (ms)
489 if (dwRet == WAIT_OBJECT_0)
496 // Thread Termination
499 PowerNotificationThreadExit()
504 SetEvent(gThreadInfo.hEventTerminate);
505 WaitForSingleObject(gThreadHandle, INFINITE);
506 CloseHandle(gThreadHandle);