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"
27 #include "afsd_flushvol.h"
28 #include "afsd_eventlog.h"
30 static FLUSHVOLTHREADINFO gThreadInfo = {0};
31 static HANDLE gThreadHandle = NULL;
34 /////////////////////////////////////////////////////////////////////
36 // Call routine found in FS.EXE to flush volume.
38 // At entry, input param is UNC string for volume,
39 // e.g. '\\afs\all\athena.mit.edu\user\m\h\mholiday'
41 // I believe that success from 'pioctl' routine
42 // indicated by return value of zero (0).
45 afsd_ServicePerformFlushVolumeCmd(char *data)
47 register afs_int32 code;
48 struct ViceIoctl blob;
50 memset(&blob, '\0', sizeof(blob));
51 code = pioctl(data, VIOC_FLUSHVOLUME, &blob, 0);
57 afsd_ServicePerformFlushVolumes()
59 CONST CHAR COLON = ':';
60 CONST CHAR SLASH = '\\';
61 CONST DWORD NETRESBUFSIZE = 16384;
62 CHAR bufMessage[1024];
67 DWORD dwNetResBufSize;
68 DWORD dwTotalVols = 0;
69 DWORD dwVolBegin, dwVolEnd;
70 DWORD dwFlushBegin, dwFlushEnd;
72 LPNETRESOURCE lpNetResBuf, lpnr;
73 PCHAR pszShareName, pc;
76 // Determine the root share name (\\AFS\ALL or \\<machine>-AFS\ALL),
77 // and the length of the server name prefix.
78 pszShareName = smb_GetSharename();
79 if (pszShareName == NULL)
81 LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_NO_SHARE_NAME, NULL);
84 pc = strrchr(pszShareName, SLASH);
85 if ((pc == NULL) || ((dwServerSize = pc - pszShareName) < 3))
87 LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_BAD_SHARE_NAME,
93 // Allocate a buffer to hold network resources returned by
94 // WNetEnumResource().
95 lpNetResBuf = malloc(NETRESBUFSIZE);
96 if (lpNetResBuf == NULL)
98 // Out of memory, give up now.
99 LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_NO_MEMORY, NULL);
104 // Initialize the flush timer. Note that GetTickCount() returns
105 // the number of milliseconds since the system started, in a DWORD,
106 // so that the value wraps around every 49.7 days. We do not bother
107 // to handle the case where the flush elapsed time is greater than
109 dwFlushBegin = GetTickCount();
111 dwRet = WNetOpenEnum(RESOURCE_CONNECTED, RESOURCETYPE_ANY, 0, NULL,
113 if (dwRet != NO_ERROR)
115 LogEventMessage(EVENTLOG_ERROR_TYPE, MSG_FLUSH_OPEN_ENUM_ERROR,
121 // Loop to enumerate network resources, and flush those associated
126 memset(lpNetResBuf, 0, NETRESBUFSIZE);
127 dwNetResBufSize = NETRESBUFSIZE;
128 dwRet = WNetEnumResource(hEnum, &dwCount,
129 lpNetResBuf, &dwNetResBufSize);
130 if (dwRet != NO_ERROR)
132 // Iterate over the returned network resources.
133 for (i = 0, lpnr = lpNetResBuf; i < dwCount; i++, lpnr++)
135 // Ensure resource has a remote name, and is connected.
136 if ((lpnr->lpRemoteName == NULL) ||
137 (lpnr->dwScope != RESOURCE_CONNECTED))
139 if ((_strnicmp(lpnr->lpRemoteName, pszShareName,
140 dwServerSize) == 0) &&
141 (lpnr->lpRemoteName[dwServerSize] == SLASH))
144 // but we don't want to flush '\\[...]afs\all'
145 if (_stricmp(lpnr->lpRemoteName,
150 dwVolBegin = GetTickCount();
151 afsRet = afsd_ServicePerformFlushVolumeCmd(lpnr->lpRemoteName);
152 dwVolEnd = GetTickCount();
155 LogTimingEvent(MSG_TIME_FLUSH_PER_VOLUME,
157 dwVolEnd - dwVolBegin);
161 LogEvent(EVENTLOG_WARNING_TYPE,
163 lpnr->lpRemoteName, NULL);
168 WNetCloseEnum(hEnum);
171 if (dwRet != ERROR_NO_MORE_ITEMS)
173 LogEventMessage(EVENTLOG_ERROR_TYPE, MSG_FLUSH_ENUM_ERROR,
178 dwFlushEnd = GetTickCount();
180 // display total volume count in Event Logger
181 sprintf(bufMessage, "%d", dwTotalVols);
182 LogTimingEvent(MSG_TIME_FLUSH_TOTAL, bufMessage,
183 dwFlushEnd - dwFlushBegin);
188 // Report a timing event to the system event log.
189 // The lpszString1 argument is the first substitution string for the
190 // given event ID. The time argument will be converted into the
191 // second substitution string.
193 LogTimingEvent(DWORD dwEventID, LPTSTR lpString1, DWORD dwTime)
197 sprintf(szTime, "%lu", dwTime);
198 LogEvent(EVENTLOG_INFORMATION_TYPE, dwEventID, lpString1, szTime,
203 /////////////////////////////////////////////////////////////////////
207 // Obtain token for the currently logged-in user.
209 // This routine looks for a window which we 'know' belongs to
210 // the shell, and from there we follow a route which leads to
211 // getting a handle on an access token owned by the shell.
213 // The return value is either a handle to a suitable token,
216 // One of the times that this function might return null
217 // is when there is no logged-in user. Other cases include
218 // insufficient access to the desktop, etc.
221 // Portions of this routine found in various newsgroups
223 HANDLE GetUserToken(DWORD access)
226 DWORD pid = 0, tid = 0;
228 // Try it the easy way first - look for a window owned by the shell on
229 // our current desktop. If we find one, use that to get the process id.
230 HWND shell = FindWindowEx(NULL, NULL, "Progman", NULL);
233 tid = GetWindowThreadProcessId(shell, &pid);
236 // We are possibly running on a private window station and desktop: we must
237 // switch to the default (which we suppose is where we will find the
241 HWINSTA saveWinSta = GetProcessWindowStation();
242 HDESK saveDesk = GetThreadDesktop(GetCurrentThreadId());
243 HWINSTA winSta = NULL;
245 BOOL changeFlag = FALSE;
246 BOOL dummy = saveWinSta != NULL &&
248 (winSta = OpenWindowStation("WinSta0", FALSE,
249 MAXIMUM_ALLOWED)) != NULL &&
250 (changeFlag = SetProcessWindowStation(winSta)) != 0 &&
251 (desk = OpenDesktop("Default", 0, FALSE,
252 MAXIMUM_ALLOWED)) != NULL &&
253 SetThreadDesktop(desk) != 0;
255 // Now find the window and process on this desktop
256 shell = FindWindowEx(NULL, NULL, "Progman", NULL);
259 tid = GetWindowThreadProcessId(shell, &pid);
262 // Restore our own window station and desktop
265 SetProcessWindowStation(saveWinSta);
266 SetThreadDesktop(saveDesk);
269 // Close temporary objects
271 CloseWindowStation(winSta);
277 // If we have a process id, use that to get the process handle and
278 // from there the process' access token.
282 HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
285 OpenProcessToken(hProc, access, &hTok) || (hTok = NULL);
290 // Return token if we got one
294 // impersonate logged-on user as client
298 DWORD dwDesiredAccess = TOKEN_ALL_ACCESS;
299 HANDLE hUserToken = GetUserToken(dwDesiredAccess);
301 if (hUserToken == NULL)
303 if (ImpersonateLoggedOnUser(hUserToken) == 0)
305 LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_IMPERSONATE_ERROR,
312 /////////////////////////////////////////////////////////////////////
317 afsd_ServiceFlushVolumesThreadProc(LPVOID lpParam)
319 FLUSHVOLTHREADINFO ThreadInfo;
320 PFLUSHVOLTHREADINFO pThreadInfo = (PFLUSHVOLTHREADINFO) lpParam;
321 HANDLE arHandles[2] = {0};
322 DWORD dwWaitState = 0;
324 // thread running - get handles
325 ThreadInfo.hEventPowerEvent = pThreadInfo->hEventPowerEvent;
326 ThreadInfo.hEventResumeMain = pThreadInfo->hEventResumeMain;
327 ThreadInfo.hEventTerminate = pThreadInfo->hEventTerminate;
330 arHandles[0] = ThreadInfo.hEventTerminate;
331 arHandles[1] = ThreadInfo.hEventPowerEvent;
336 // wait for an event to happen
337 dwWaitState = WaitForMultipleObjectsEx(2, arHandles, FALSE, INFINITE, FALSE);
342 // termination signaled
348 case WAIT_OBJECT_0+1:
351 if (ImpersonateClient())
353 afsd_ServicePerformFlushVolumes();
356 ResetEvent(ThreadInfo.hEventPowerEvent);
359 case WAIT_ABANDONED_0:
360 case WAIT_ABANDONED_0+1:
361 case WAIT_IO_COMPLETION:
364 LogEvent(EVENTLOG_WARNING_TYPE,
365 MSG_FLUSH_UNEXPECTED_EVENT, NULL);
370 // signal back to waiting mainline
371 SetEvent(ThreadInfo.hEventResumeMain);
375 // I suppose we never get here
379 /////////////////////////////////////////////////////////////////////
381 // Mainline thread routines
385 CheckAndCloseHandle(HANDLE thisHandle)
387 if (thisHandle != NULL)
389 CloseHandle(thisHandle);
398 PowerNotificationThreadCreate()
400 BOOL bSuccess = FALSE;
401 DWORD dwThreadId = 0;
405 // create power event notification event
406 // bManualReset=TRUE, bInitialState=FALSE
407 gThreadInfo.hEventPowerEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
408 if (gThreadInfo.hEventPowerEvent == NULL)
411 // create mainline resume event
412 // bManualReset=FALSE, bInitialState=FALSE
413 gThreadInfo.hEventResumeMain = CreateEvent(NULL, FALSE, FALSE, NULL);
414 if (gThreadInfo.hEventResumeMain == NULL)
417 // create thread terminate event
418 // bManualReset=FALSE, bInitialState=FALSE
419 gThreadInfo.hEventTerminate = CreateEvent(NULL, FALSE, FALSE, NULL);
420 if (gThreadInfo.hEventTerminate == NULL)
423 // good so far - create thread
424 gThreadHandle = CreateThread(NULL, 0,
425 afsd_ServiceFlushVolumesThreadProc,
426 (LPVOID) &gThreadInfo,
439 CheckAndCloseHandle(gThreadInfo.hEventPowerEvent);
440 CheckAndCloseHandle(gThreadInfo.hEventResumeMain);
441 CheckAndCloseHandle(gThreadInfo.hEventTerminate);
442 CheckAndCloseHandle(gThreadHandle);
449 // Thread Notification
452 PowerNotificationThreadNotify()
457 // Notify thread of power event, and wait 19 seconds
458 dwRet = SignalObjectAndWait(
459 gThreadInfo.hEventPowerEvent, // object to signal
460 gThreadInfo.hEventResumeMain, // object to watch
461 19*1000, // timeout (ms)
465 if (dwRet == WAIT_OBJECT_0)
472 // Thread Termination
475 PowerNotificationThreadExit()
480 SetEvent(gThreadInfo.hEventTerminate);
481 CloseHandle(gThreadHandle);