winecoreaudio: Specialize wodHelper_BeginWaveHdr for its two callers and simplify.
[wine] / programs / wineboot / wineboot.c
1 /*
2  * Copyright (C) 2002 Andreas Mohr
3  * Copyright (C) 2002 Shachar Shemesh
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 /* Wine "bootup" handler application
20  *
21  * This app handles the various "hooks" windows allows for applications to perform
22  * as part of the bootstrap process. Theses are roughly devided into three types.
23  * Knowledge base articles that explain this are 137367, 179365, 232487 and 232509.
24  * Also, 119941 has some info on grpconv.exe
25  * The operations performed are (by order of execution):
26  *
27  * Preboot (prior to fully loading the Windows kernel):
28  * - wininit.exe (rename operations left in wininit.ini - Win 9x only)
29  * - PendingRenameOperations (rename operations left in the registry - Win NT+ only)
30  *
31  * Startup (before the user logs in)
32  * - Services (NT, ?semi-synchronous?, not implemented yet)
33  * - HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce (9x, asynch)
34  * - HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices (9x, asynch)
35  * 
36  * After log in
37  * - HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce (all, synch)
38  * - HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run (all, asynch)
39  * - HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run (all, asynch)
40  * - Startup folders (all, ?asynch?, no imp)
41  * - HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce (all, asynch)
42  *   
43  * Somewhere in there is processing the RunOnceEx entries (also no imp)
44  * 
45  * Bugs:
46  * - If a pending rename registry does not start with \??\ the entry is
47  *   processed anyways. I'm not sure that is the Windows behaviour.
48  * - Need to check what is the windows behaviour when trying to delete files
49  *   and directories that are read-only
50  * - In the pending rename registry processing - there are no traces of the files
51  *   processed (requires translations from Unicode to Ansi).
52  */
53
54 #include "config.h"
55 #include "wine/port.h"
56
57 #define WIN32_LEAN_AND_MEAN
58
59 #include <stdio.h>
60 #ifdef HAVE_GETOPT_H
61 # include <getopt.h>
62 #endif
63 #include <windows.h>
64 #include <wine/debug.h>
65
66 WINE_DEFAULT_DEBUG_CHANNEL(wineboot);
67
68 #define MAX_LINE_LENGTH (2*MAX_PATH+2)
69
70 extern BOOL shutdown_close_windows( BOOL force );
71 extern void kill_processes( BOOL kill_desktop );
72
73 static BOOL GetLine( HANDLE hFile, char *buf, size_t buflen )
74 {
75     unsigned int i=0;
76     DWORD r;
77     buf[0]='\0';
78
79     do
80     {
81         DWORD read;
82         if( !ReadFile( hFile, buf, 1, &read, NULL ) || read!=1 )
83         {
84             return FALSE;
85         }
86
87     } while( isspace( *buf ) );
88
89     while( buf[i]!='\n' && i<=buflen &&
90             ReadFile( hFile, buf+i+1, 1, &r, NULL ) )
91     {
92         ++i;
93     }
94
95
96     if( buf[i]!='\n' )
97     {
98         return FALSE;
99     }
100
101     if( i>0 && buf[i-1]=='\r' )
102         --i;
103
104     buf[i]='\0';
105
106     return TRUE;
107 }
108
109 /* Performs the rename operations dictated in %SystemRoot%\Wininit.ini.
110  * Returns FALSE if there was an error, or otherwise if all is ok.
111  */
112 static BOOL wininit(void)
113 {
114     const char * const RENAME_FILE="wininit.ini";
115     const char * const RENAME_FILE_TO="wininit.bak";
116     const char * const RENAME_FILE_SECTION="[rename]";
117     char buffer[MAX_LINE_LENGTH];
118     HANDLE hFile;
119
120
121     hFile=CreateFileA(RENAME_FILE, GENERIC_READ,
122                     FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
123                     NULL );
124     
125     if( hFile==INVALID_HANDLE_VALUE )
126     {
127         DWORD err=GetLastError();
128         
129         if( err==ERROR_FILE_NOT_FOUND )
130         {
131             /* No file - nothing to do. Great! */
132             WINE_TRACE("Wininit.ini not present - no renaming to do\n");
133
134             return TRUE;
135         }
136
137         WINE_ERR("There was an error in reading wininit.ini file - %d\n",
138                 GetLastError() );
139
140         return FALSE;
141     }
142
143     printf("Wine is finalizing your software installation. This may take a few minutes,\n");
144     printf("though it never actually does.\n");
145
146     while( GetLine( hFile, buffer, sizeof(buffer) ) &&
147             lstrcmpiA(buffer,RENAME_FILE_SECTION)!=0  )
148         ; /* Read the lines until we match the rename section */
149
150     while( GetLine( hFile, buffer, sizeof(buffer) ) && buffer[0]!='[' )
151     {
152         /* First, make sure this is not a comment */
153         if( buffer[0]!=';' && buffer[0]!='\0' )
154         {
155             char * value;
156
157             value=strchr(buffer, '=');
158
159             if( value==NULL )
160             {
161                 WINE_WARN("Line with no \"=\" in it in wininit.ini - %s\n",
162                         buffer);
163             } else
164             {
165                 /* split the line into key and value */
166                 *(value++)='\0';
167
168                 if( lstrcmpiA( "NUL", buffer )==0 )
169                 {
170                     WINE_TRACE("Deleting file \"%s\"\n", value );
171                     /* A file to delete */
172                     if( !DeleteFileA( value ) )
173                         WINE_WARN("Error deleting file \"%s\"\n", value);
174                 } else
175                 {
176                     WINE_TRACE("Renaming file \"%s\" to \"%s\"\n", value,
177                             buffer );
178
179                     if( !MoveFileExA(value, buffer, MOVEFILE_COPY_ALLOWED|
180                             MOVEFILE_REPLACE_EXISTING) )
181                     {
182                         WINE_WARN("Error renaming \"%s\" to \"%s\"\n", value,
183                                 buffer );
184                     }
185                 }
186             }
187         }
188     }
189
190     CloseHandle( hFile );
191
192     if( !MoveFileExA( RENAME_FILE, RENAME_FILE_TO, MOVEFILE_REPLACE_EXISTING) )
193     {
194         WINE_ERR("Couldn't rename wininit.ini, error %d\n", GetLastError() );
195
196         return FALSE;
197     }
198
199     return TRUE;
200 }
201
202 static BOOL pendingRename(void)
203 {
204     static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
205                                       'F','i','l','e','R','e','n','a','m','e',
206                                       'O','p','e','r','a','t','i','o','n','s',0};
207     static const WCHAR SessionW[] = { 'S','y','s','t','e','m','\\',
208                                      'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
209                                      'C','o','n','t','r','o','l','\\',
210                                      'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
211     WCHAR *buffer=NULL;
212     const WCHAR *src=NULL, *dst=NULL;
213     DWORD dataLength=0;
214     HKEY hSession=NULL;
215     DWORD res;
216
217     WINE_TRACE("Entered\n");
218
219     if( (res=RegOpenKeyExW( HKEY_LOCAL_MACHINE, SessionW, 0, KEY_ALL_ACCESS, &hSession ))
220             !=ERROR_SUCCESS )
221     {
222         if( res==ERROR_FILE_NOT_FOUND )
223         {
224             WINE_TRACE("The key was not found - skipping\n");
225             res=TRUE;
226         }
227         else
228         {
229             WINE_ERR("Couldn't open key, error %d\n", res );
230             res=FALSE;
231         }
232
233         goto end;
234     }
235
236     res=RegQueryValueExW( hSession, ValueName, NULL, NULL /* The value type does not really interest us, as it is not
237                                                              truly a REG_MULTI_SZ anyways */,
238             NULL, &dataLength );
239     if( res==ERROR_FILE_NOT_FOUND )
240     {
241         /* No value - nothing to do. Great! */
242         WINE_TRACE("Value not present - nothing to rename\n");
243         res=TRUE;
244         goto end;
245     }
246
247     if( res!=ERROR_SUCCESS )
248     {
249         WINE_ERR("Couldn't query value's length (%d)\n", res );
250         res=FALSE;
251         goto end;
252     }
253
254     buffer=HeapAlloc( GetProcessHeap(),0,dataLength );
255     if( buffer==NULL )
256     {
257         WINE_ERR("Couldn't allocate %u bytes for the value\n", dataLength );
258         res=FALSE;
259         goto end;
260     }
261
262     res=RegQueryValueExW( hSession, ValueName, NULL, NULL, (LPBYTE)buffer, &dataLength );
263     if( res!=ERROR_SUCCESS )
264     {
265         WINE_ERR("Couldn't query value after successfully querying before (%u),\n"
266                 "please report to wine-devel@winehq.org\n", res);
267         res=FALSE;
268         goto end;
269     }
270
271     /* Make sure that the data is long enough and ends with two NULLs. This
272      * simplifies the code later on.
273      */
274     if( dataLength<2*sizeof(buffer[0]) ||
275             buffer[dataLength/sizeof(buffer[0])-1]!='\0' ||
276             buffer[dataLength/sizeof(buffer[0])-2]!='\0' )
277     {
278         WINE_ERR("Improper value format - doesn't end with NULL\n");
279         res=FALSE;
280         goto end;
281     }
282
283     for( src=buffer; (src-buffer)*sizeof(src[0])<dataLength && *src!='\0';
284             src=dst+lstrlenW(dst)+1 )
285     {
286         DWORD dwFlags=0;
287
288         WINE_TRACE("processing next command\n");
289
290         dst=src+lstrlenW(src)+1;
291
292         /* We need to skip the \??\ header */
293         if( src[0]=='\\' && src[1]=='?' && src[2]=='?' && src[3]=='\\' )
294             src+=4;
295
296         if( dst[0]=='!' )
297         {
298             dwFlags|=MOVEFILE_REPLACE_EXISTING;
299             dst++;
300         }
301
302         if( dst[0]=='\\' && dst[1]=='?' && dst[2]=='?' && dst[3]=='\\' )
303             dst+=4;
304
305         if( *dst!='\0' )
306         {
307             /* Rename the file */
308             MoveFileExW( src, dst, dwFlags );
309         } else
310         {
311             /* Delete the file or directory */
312             if( (res=GetFileAttributesW(src))!=INVALID_FILE_ATTRIBUTES )
313             {
314                 if( (res&FILE_ATTRIBUTE_DIRECTORY)==0 )
315                 {
316                     /* It's a file */
317                     DeleteFileW(src);
318                 } else
319                 {
320                     /* It's a directory */
321                     RemoveDirectoryW(src);
322                 }
323             } else
324             {
325                 WINE_ERR("couldn't get file attributes (%d)\n", GetLastError() );
326             }
327         }
328     }
329
330     if((res=RegDeleteValueW(hSession, ValueName))!=ERROR_SUCCESS )
331     {
332         WINE_ERR("Error deleting the value (%u)\n", GetLastError() );
333         res=FALSE;
334     } else
335         res=TRUE;
336     
337 end:
338     HeapFree(GetProcessHeap(), 0, buffer);
339
340     if( hSession!=NULL )
341         RegCloseKey( hSession );
342
343     return res;
344 }
345
346 enum runkeys {
347     RUNKEY_RUN, RUNKEY_RUNONCE, RUNKEY_RUNSERVICES, RUNKEY_RUNSERVICESONCE
348 };
349
350 const WCHAR runkeys_names[][30]=
351 {
352     {'R','u','n',0},
353     {'R','u','n','O','n','c','e',0},
354     {'R','u','n','S','e','r','v','i','c','e','s',0},
355     {'R','u','n','S','e','r','v','i','c','e','s','O','n','c','e',0}
356 };
357
358 #define INVALID_RUNCMD_RETURN -1
359 /*
360  * This function runs the specified command in the specified dir.
361  * [in,out] cmdline - the command line to run. The function may change the passed buffer.
362  * [in] dir - the dir to run the command in. If it is NULL, then the current dir is used.
363  * [in] wait - whether to wait for the run program to finish before returning.
364  * [in] minimized - Whether to ask the program to run minimized.
365  *
366  * Returns:
367  * If running the process failed, returns INVALID_RUNCMD_RETURN. Use GetLastError to get the error code.
368  * If wait is FALSE - returns 0 if successful.
369  * If wait is TRUE - returns the program's return value.
370  */
371 static DWORD runCmd(LPWSTR cmdline, LPCWSTR dir, BOOL wait, BOOL minimized)
372 {
373     STARTUPINFOW si;
374     PROCESS_INFORMATION info;
375     DWORD exit_code=0;
376
377     memset(&si, 0, sizeof(si));
378     si.cb=sizeof(si);
379     if( minimized )
380     {
381         si.dwFlags=STARTF_USESHOWWINDOW;
382         si.wShowWindow=SW_MINIMIZE;
383     }
384     memset(&info, 0, sizeof(info));
385
386     if( !CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, dir, &si, &info) )
387     {
388         WINE_ERR("Failed to run command %s (%d)\n", wine_dbgstr_w(cmdline),
389                  GetLastError() );
390
391         return INVALID_RUNCMD_RETURN;
392     }
393
394     WINE_TRACE("Successfully ran command %s - Created process handle %p\n",
395                wine_dbgstr_w(cmdline), info.hProcess );
396
397     if(wait)
398     {   /* wait for the process to exit */
399         WaitForSingleObject(info.hProcess, INFINITE);
400         GetExitCodeProcess(info.hProcess, &exit_code);
401     }
402
403     CloseHandle( info.hProcess );
404
405     return exit_code;
406 }
407
408 /*
409  * Process a "Run" type registry key.
410  * hkRoot is the HKEY from which "Software\Microsoft\Windows\CurrentVersion" is
411  *      opened.
412  * szKeyName is the key holding the actual entries.
413  * bDelete tells whether we should delete each value right before executing it.
414  * bSynchronous tells whether we should wait for the prog to complete before
415  *      going on to the next prog.
416  */
417 static BOOL ProcessRunKeys( HKEY hkRoot, LPCWSTR szKeyName, BOOL bDelete,
418         BOOL bSynchronous )
419 {
420     static const WCHAR WINKEY_NAME[]={'S','o','f','t','w','a','r','e','\\',
421         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
422         'C','u','r','r','e','n','t','V','e','r','s','i','o','n',0};
423     HKEY hkWin=NULL, hkRun=NULL;
424     DWORD res=ERROR_SUCCESS;
425     DWORD i, nMaxCmdLine=0, nMaxValue=0;
426     WCHAR *szCmdLine=NULL;
427     WCHAR *szValue=NULL;
428
429     if (hkRoot==HKEY_LOCAL_MACHINE)
430         WINE_TRACE("processing %s entries under HKLM\n",wine_dbgstr_w(szKeyName) );
431     else
432         WINE_TRACE("processing %s entries under HKCU\n",wine_dbgstr_w(szKeyName) );
433
434     if( (res=RegOpenKeyExW( hkRoot, WINKEY_NAME, 0, KEY_READ, &hkWin ))!=ERROR_SUCCESS )
435     {
436         WINE_ERR("RegOpenKey failed on Software\\Microsoft\\Windows\\CurrentVersion (%d)\n",
437                 res);
438
439         goto end;
440     }
441
442     if( (res=RegOpenKeyExW( hkWin, szKeyName, 0, bDelete?KEY_ALL_ACCESS:KEY_READ, &hkRun ))!=
443             ERROR_SUCCESS)
444     {
445         if( res==ERROR_FILE_NOT_FOUND )
446         {
447             WINE_TRACE("Key doesn't exist - nothing to be done\n");
448
449             res=ERROR_SUCCESS;
450         }
451         else
452             WINE_ERR("RegOpenKey failed on run key (%d)\n", res);
453
454         goto end;
455     }
456     
457     if( (res=RegQueryInfoKeyW( hkRun, NULL, NULL, NULL, NULL, NULL, NULL, &i, &nMaxValue,
458                     &nMaxCmdLine, NULL, NULL ))!=ERROR_SUCCESS )
459     {
460         WINE_ERR("Couldn't query key info (%d)\n", res );
461
462         goto end;
463     }
464
465     if( i==0 )
466     {
467         WINE_TRACE("No commands to execute.\n");
468
469         res=ERROR_SUCCESS;
470         goto end;
471     }
472     
473     if( (szCmdLine=HeapAlloc(GetProcessHeap(),0,nMaxCmdLine))==NULL )
474     {
475         WINE_ERR("Couldn't allocate memory for the commands to be executed\n");
476
477         res=ERROR_NOT_ENOUGH_MEMORY;
478         goto end;
479     }
480
481     if( (szValue=HeapAlloc(GetProcessHeap(),0,(++nMaxValue)*sizeof(*szValue)))==NULL )
482     {
483         WINE_ERR("Couldn't allocate memory for the value names\n");
484
485         res=ERROR_NOT_ENOUGH_MEMORY;
486         goto end;
487     }
488     
489     while( i>0 )
490     {
491         DWORD nValLength=nMaxValue, nDataLength=nMaxCmdLine;
492         DWORD type;
493
494         --i;
495
496         if( (res=RegEnumValueW( hkRun, i, szValue, &nValLength, 0, &type,
497                         (LPBYTE)szCmdLine, &nDataLength ))!=ERROR_SUCCESS )
498         {
499             WINE_ERR("Couldn't read in value %d - %d\n", i, res );
500
501             continue;
502         }
503
504         if( bDelete && (res=RegDeleteValueW( hkRun, szValue ))!=ERROR_SUCCESS )
505         {
506             WINE_ERR("Couldn't delete value - %d, %d. Running command anyways.\n", i, res );
507         }
508         
509         if( type!=REG_SZ )
510         {
511             WINE_ERR("Incorrect type of value #%d (%d)\n", i, type );
512
513             continue;
514         }
515
516         if( (res=runCmd(szCmdLine, NULL, bSynchronous, FALSE ))==INVALID_RUNCMD_RETURN )
517         {
518             WINE_ERR("Error running cmd #%d (%d)\n", i, GetLastError() );
519         }
520
521         WINE_TRACE("Done processing cmd #%d\n", i);
522     }
523
524     res=ERROR_SUCCESS;
525
526 end:
527     if( hkRun!=NULL )
528         RegCloseKey( hkRun );
529     if( hkWin!=NULL )
530         RegCloseKey( hkWin );
531
532     WINE_TRACE("done\n");
533
534     return res==ERROR_SUCCESS?TRUE:FALSE;
535 }
536
537 /*
538  * WFP is Windows File Protection, in NT5 and Windows 2000 it maintains a cache
539  * of known good dlls and scans through and replaces corrupted DLLs with these
540  * known good versions. The only programs that should install into this dll
541  * cache are Windows Updates and IE (which is treated like a Windows Update)
542  *
543  * Implementing this allows installing ie in win2k mode to actaully install the
544  * system dlls that we expect and need
545  */
546 static int ProcessWindowsFileProtection(void)
547 {
548     WIN32_FIND_DATA finddata;
549     LPSTR custom_dllcache = NULL;
550     static CHAR default_dllcache[] = "C:\\Windows\\System32\\dllcache";
551     HANDLE find_handle;
552     BOOL find_rc;
553     DWORD rc;
554     HKEY hkey;
555     LPSTR dllcache;
556     CHAR find_string[MAX_PATH];
557     CHAR windowsdir[MAX_PATH];
558
559     rc = RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", &hkey );
560     if (rc == ERROR_SUCCESS)
561     {
562         DWORD sz = 0;
563         rc = RegQueryValueEx( hkey, "SFCDllCacheDir", 0, NULL, NULL, &sz);
564         if (rc == ERROR_MORE_DATA)
565         {
566             sz++;
567             custom_dllcache = HeapAlloc(GetProcessHeap(),0,sz);
568             RegQueryValueEx( hkey, "SFCDllCacheDir", 0, NULL, (LPBYTE)custom_dllcache, &sz);
569         }
570     }
571     RegCloseKey(hkey);
572
573     if (custom_dllcache)
574         dllcache = custom_dllcache;
575     else
576         dllcache = default_dllcache;
577
578     strcpy(find_string,dllcache);
579     strcat(find_string,"\\*.*");
580
581     GetWindowsDirectory(windowsdir,MAX_PATH);
582
583     find_handle = FindFirstFile(find_string,&finddata);
584     find_rc = find_handle != INVALID_HANDLE_VALUE;
585     while (find_rc)
586     {
587         CHAR targetpath[MAX_PATH];
588         CHAR currentpath[MAX_PATH];
589         UINT sz;
590         UINT sz2;
591         CHAR tempfile[MAX_PATH];
592
593         if (strcmp(finddata.cFileName,".") == 0 ||
594             strcmp(finddata.cFileName,"..") == 0)
595         {
596             find_rc = FindNextFile(find_handle,&finddata);
597             continue;
598         }
599
600         sz = MAX_PATH;
601         sz2 = MAX_PATH;
602         VerFindFile(VFFF_ISSHAREDFILE, finddata.cFileName, windowsdir, 
603                 windowsdir, currentpath, &sz, targetpath,&sz2);
604         sz = MAX_PATH;
605         rc = VerInstallFile(0, finddata.cFileName, finddata.cFileName,
606                             dllcache, targetpath, currentpath, tempfile,&sz);
607         if (rc != ERROR_SUCCESS)
608         {
609             WINE_ERR("WFP: %s error 0x%x\n",finddata.cFileName,rc);
610             DeleteFile(tempfile);
611         }
612         find_rc = FindNextFile(find_handle,&finddata);
613     }
614     FindClose(find_handle);
615     HeapFree(GetProcessHeap(),0,custom_dllcache);
616     return 1;
617 }
618
619 static void usage(void)
620 {
621     WINE_MESSAGE( "Usage: wineboot [options]\n" );
622     WINE_MESSAGE( "Options;\n" );
623     WINE_MESSAGE( "    -h,--help         Display this help message\n" );
624     WINE_MESSAGE( "    -e,--end-session  End the current session cleanly\n" );
625     WINE_MESSAGE( "    -f,--force        Force exit for processes that don't exit cleanly\n" );
626     WINE_MESSAGE( "    -k,--kill         Kill running processes without any cleanup\n" );
627     WINE_MESSAGE( "    -r,--restart      Restart only, don't do normal startup operations\n" );
628     WINE_MESSAGE( "    -s,--shutdown     Shutdown only, don't reboot\n" );
629 }
630
631 static const char short_options[] = "efhkrs";
632
633 static const struct option long_options[] =
634 {
635     { "help",        0, 0, 'h' },
636     { "end-session", 0, 0, 'e' },
637     { "force",       0, 0, 'f' },
638     { "kill",        0, 0, 'k' },
639     { "restart",     0, 0, 'r' },
640     { "shutdown",    0, 0, 's' },
641     { NULL,          0, 0, 0 }
642 };
643
644 struct op_mask {
645     BOOL w9xonly; /* Perform only operations done on Windows 9x */
646     BOOL ntonly; /* Perform only operations done on Windows NT */
647     BOOL startup; /* Perform the operations that are performed every boot */
648     BOOL preboot; /* Perform file renames typically done before the system starts */
649     BOOL prelogin; /* Perform the operations typically done before the user logs in */
650     BOOL postlogin; /* Operations done after login */
651 };
652
653 static const struct op_mask SESSION_START={FALSE, FALSE, TRUE, TRUE, TRUE, TRUE},
654     SETUP={FALSE, FALSE, FALSE, TRUE, TRUE, TRUE};
655
656 int main( int argc, char *argv[] )
657 {
658     struct op_mask ops = SESSION_START; /* Which of the ops do we want to perform? */
659     /* First, set the current directory to SystemRoot */
660     TCHAR gen_path[MAX_PATH];
661     DWORD res;
662     int optc;
663     int end_session = 0, force = 0, kill = 0, restart = 0, shutdown = 0;
664
665     res=GetWindowsDirectory( gen_path, sizeof(gen_path) );
666     
667     if( res==0 )
668     {
669         WINE_ERR("Couldn't get the windows directory - error %d\n",
670                 GetLastError() );
671
672         return 100;
673     }
674
675     if( res>=sizeof(gen_path) )
676     {
677         WINE_ERR("Windows path too long (%d)\n", res );
678
679         return 100;
680     }
681
682     if( !SetCurrentDirectory( gen_path ) )
683     {
684         WINE_ERR("Cannot set the dir to %s (%d)\n", gen_path, GetLastError() );
685
686         return 100;
687     }
688
689
690     while ((optc = getopt_long(argc, argv, short_options, long_options, NULL )) != -1)
691     {
692         switch(optc)
693         {
694         case 'e': end_session = 1; break;
695         case 'f': force = 1; break;
696         case 'k': kill = 1; break;
697         case 'r': restart = 1; break;
698         case 's': shutdown = 1; break;
699         case 'h': usage(); return 0;
700         case '?': usage(); return 1;
701         }
702     }
703
704     if (end_session)
705     {
706         if (!shutdown_close_windows( force )) return 1;
707     }
708
709     if (end_session || kill) kill_processes( shutdown );
710
711     if (shutdown) return 0;
712
713     if (restart) ops = SETUP;
714
715     /* Perform the ops by order, stopping if one fails, skipping if necessary */
716     /* Shachar: Sorry for the perl syntax */
717     res=(ops.ntonly || !ops.preboot || wininit())&&
718         (ops.w9xonly || !ops.preboot || pendingRename()) &&
719         (ops.ntonly || !ops.prelogin ||
720          ProcessRunKeys( HKEY_LOCAL_MACHINE, runkeys_names[RUNKEY_RUNSERVICESONCE],
721                 TRUE, FALSE )) &&
722         (ops.ntonly || !ops.prelogin ||
723          ProcessWindowsFileProtection()) &&
724         (ops.ntonly || !ops.prelogin || !ops.startup ||
725          ProcessRunKeys( HKEY_LOCAL_MACHINE, runkeys_names[RUNKEY_RUNSERVICES],
726                 FALSE, FALSE )) &&
727         (!ops.postlogin ||
728          ProcessRunKeys( HKEY_LOCAL_MACHINE, runkeys_names[RUNKEY_RUNONCE],
729                 TRUE, TRUE )) &&
730         (!ops.postlogin || !ops.startup ||
731          ProcessRunKeys( HKEY_LOCAL_MACHINE, runkeys_names[RUNKEY_RUN],
732                 FALSE, FALSE )) &&
733         (!ops.postlogin || !ops.startup ||
734          ProcessRunKeys( HKEY_CURRENT_USER, runkeys_names[RUNKEY_RUN],
735                 FALSE, FALSE ));
736
737     WINE_TRACE("Operation done\n");
738
739     return res?0:101;
740 }