Changeset 95139 in vbox for trunk/src/VBox
- Timestamp:
- May 30, 2022 5:19:09 PM (3 years ago)
- Location:
- trunk/src/VBox/Frontends/VBoxAutostart
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostart-posix.cpp
r95105 r95139 248 248 } 249 249 250 static void displayHeader()251 {252 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Autostart " VBOX_VERSION_STRING "\n"253 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"254 "All rights reserved.\n\n");255 }256 257 250 /** 258 * Displays the help.251 * Shows the help. 259 252 * 260 253 * @param pszImage Name of program name (image). 261 254 */ 262 static void displayHelp(const char *pszImage)255 static void showHelp(const char *pszImage) 263 256 { 264 257 AssertPtrReturnVoid(pszImage); 265 258 266 displayHeader();259 autostartSvcShowHeader(); 267 260 268 261 RTStrmPrintf(g_pStdErr, 269 262 "Usage: %s [-v|--verbose] [-h|-?|--help]\n" 263 " [-V|--version]\n" 270 264 " [-F|--logfile=<file>] [-R|--logrotate=<num>]\n" 271 265 " [-S|--logsize=<bytes>] [-I|--loginterval=<seconds>]\n" … … 282 276 { 283 277 case 'h': 284 pcszDescr = "Print this help message and exit.";278 pcszDescr = "Prints this help message and exit."; 285 279 break; 286 280 … … 310 304 pcszDescr = "Name of the configuration file for the global overrides."; 311 305 break; 306 307 case 'V': 308 pcszDescr = "Shows the service version."; 309 break; 310 312 311 default: 313 312 AssertFailedBreakStmt(pcszDescr = ""); … … 357 356 { 358 357 case 'h': 359 displayHelp(argv[0]);360 return 0;358 showHelp(argv[0]); 359 return RTEXITCODE_SUCCESS; 361 360 362 361 case 'v': … … 370 369 #endif 371 370 case 'V': 372 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());373 return 0;371 autostartSvcShowVersion(false); 372 return RTEXITCODE_SUCCESS; 374 373 375 374 case 'F': … … 412 411 if (!fStart && !fStop) 413 412 { 414 displayHelp(argv[0]);413 showHelp(argv[0]); 415 414 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Either --start or --stop must be present"); 416 415 } 417 416 else if (fStart && fStop) 418 417 { 419 displayHelp(argv[0]);418 showHelp(argv[0]); 420 419 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--start or --stop are mutually exclusive"); 421 420 } … … 423 422 if (!pszConfigFile) 424 423 { 425 displayHelp(argv[0]);424 showHelp(argv[0]); 426 425 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--config <config file> is missing"); 427 426 } 428 427 429 428 if (!fQuiet) 430 displayHeader();429 autostartSvcShowHeader(); 431 430 432 431 PCFGAST pCfgAst = NULL; … … 544 543 return RTEXITCODE_FAILURE; 545 544 546 RTEXITCODE rcExit;547 545 if (fStart) 548 rc Exit= autostartStartMain(pCfgAstUser);546 rc = autostartStartMain(pCfgAstUser); 549 547 else 550 548 { 551 549 Assert(fStop); 552 rc Exit= autostartStopMain(pCfgAstUser);550 rc = autostartStopMain(pCfgAstUser); 553 551 } 554 552 555 553 autostartConfigAstDestroy(pCfgAst); 556 554 NativeEventQueue::getMainEventQueue()->processEventQueue(0); 557 558 555 autostartShutdown(); 559 return rcExit; 556 557 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; 560 558 } 561 559 -
trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostart-win.cpp
r95103 r95139 20 20 * Header Files * 21 21 *********************************************************************************************************************************/ 22 #include <iprt/buildconfig.h>23 22 #include <iprt/dir.h> 24 23 #include <iprt/env.h> 25 #include <iprt/err core.h>24 #include <iprt/err.h> 26 25 #include <iprt/getopt.h> 27 26 #include <iprt/initterm.h> … … 51 50 52 51 #include <VBox/log.h> 53 #include <VBox/version.h>54 52 55 53 #include "VBoxAutostart.h" … … 261 259 DECLHIDDEN(void) autostartSvcOsLogStr(const char *pszMsg, AUTOSTARTLOGTYPE enmLogType) 262 260 { 263 /* write it to the release log too*/261 /* write it to the console + release log too (if configured). */ 264 262 LogRel(("%s", pszMsg)); 265 263 264 /** @todo r=andy Only (un)register source once? */ 266 265 HANDLE hEventLog = RegisterEventSourceA(NULL /* local computer */, "VBoxAutostartSvc"); 267 266 AssertReturnVoid(hEventLog != NULL); … … 274 273 { 275 274 case AUTOSTARTLOGTYPE_INFO: 275 RTStrmPrintf(g_pStdOut, "%s", pszMsg); 276 276 wType = 0; 277 277 break; 278 278 case AUTOSTARTLOGTYPE_ERROR: 279 RTStrmPrintf(g_pStdErr, "Error: %s", pszMsg); 279 280 wType = EVENTLOG_ERROR_TYPE; 280 281 break; 281 282 case AUTOSTARTLOGTYPE_WARNING: 283 RTStrmPrintf(g_pStdOut, "Warning: %s", pszMsg); 282 284 wType = EVENTLOG_WARNING_TYPE; 283 285 break; 284 286 case AUTOSTARTLOGTYPE_VERBOSE: 285 if (!g_cVerbosity) 286 return; 287 RTStrmPrintf(g_pStdOut, "%s", pszMsg); 287 288 wType = EVENTLOG_INFORMATION_TYPE; 288 289 break; 289 290 default: 290 AssertMsgFailed(("Invalid log type %d\n", enmLogType)); 291 } 292 291 AssertMsgFailed(("Invalid log type %#x\n", enmLogType)); 292 break; 293 } 294 295 /** @todo r=andy Why ANSI and not Unicode (xxxW)? */ 293 296 BOOL fRc = ReportEventA(hEventLog, /* hEventLog */ 294 297 wType, /* wType */ … … 300 303 apsz, /* lpStrings */ 301 304 NULL); /* lpRawData */ 302 AssertMsg(fRc, (" %u\n", GetLastError()));NOREF(fRc);305 AssertMsg(fRc, ("ReportEventA failed with %ld\n", GetLastError())); RT_NOREF(fRc); 303 306 DeregisterEventSource(hEventLog); 304 307 } … … 342 345 * @returns Valid service handle on success. 343 346 * NULL on failure, will display an error message unless it's ignored. 347 * Use GetLastError() to find out what the last Windows error was. 344 348 * 345 349 * @param pszAction The action which is requesting access to the service. … … 364 368 else 365 369 { 366 DWORD err= GetLastError();367 bool fIgnored = false;370 DWORD const dwErr = GetLastError(); 371 bool fIgnored = false; 368 372 va_list va; 369 373 va_start(va, cIgnoredErrors); 370 374 while (!fIgnored && cIgnoredErrors-- > 0) 371 fIgnored = (DWORD)va_arg(va, int) == err;375 fIgnored = (DWORD)va_arg(va, int) == dwErr; 372 376 va_end(va); 373 377 if (!fIgnored) 374 378 { 375 switch ( err)379 switch (dwErr) 376 380 { 377 381 case ERROR_ACCESS_DENIED: … … 383 387 break; 384 388 default: 385 autostartSvcDisplayError("%s - OpenService failure : %d\n", pszAction, err);389 autostartSvcDisplayError("%s - OpenService failure, rc=%Rrc (%#x)\n", RTErrConvertFromWin32(dwErr), dwErr); 386 390 break; 387 391 } … … 389 393 390 394 CloseServiceHandle(hSCM); 391 SetLastError( err);395 SetLastError(dwErr); 392 396 } 393 397 return hSvc; … … 472 476 * @param argv The action argument vector. 473 477 */ 474 static intautostartSvcWinDelete(int argc, char **argv)478 static RTEXITCODE autostartSvcWinDelete(int argc, char **argv) 475 479 { 476 480 /* … … 508 512 int vrc = autostartGetServiceName(pszUser, sServiceName); 509 513 if (RT_FAILURE(vrc)) 510 return autostartSvcDisplayError("delete - DeleteService failed, service name for user %s can 514 return autostartSvcDisplayError("delete - DeleteService failed, service name for user %s cannot be constructed.\n", 511 515 pszUser); 512 516 /* 513 * Create the service.517 * Delete the service. 514 518 */ 515 RTEXITCODE rc = RTEXITCODE_FAILURE; 516 SC_HANDLE hSvc = autostartSvcWinOpenService(com::Bstr(sServiceName).raw(), "delete", SERVICE_CHANGE_CONFIG, DELETE, 517 1, ERROR_SERVICE_DOES_NOT_EXIST); 519 RTEXITCODE rcExit = RTEXITCODE_FAILURE; 520 SC_HANDLE hSvc = autostartSvcWinOpenService(com::Bstr(sServiceName).raw(), "delete", SERVICE_CHANGE_CONFIG, DELETE, 0); 518 521 if (hSvc) 519 522 { 520 523 if (DeleteService(hSvc)) 521 524 { 522 RTPrintf("Successfully deleted the %s service.\n", sServiceName.c_str()); 523 rc = RTEXITCODE_SUCCESS; 525 if (g_cVerbosity) 526 RTPrintf("Successfully deleted the %s service.\n", sServiceName.c_str()); 527 rcExit = RTEXITCODE_SUCCESS; 524 528 } 525 529 else 526 autostartSvcDisplayError("delete - DeleteService failed, err=%d.\n", GetLastError()); 530 { 531 DWORD const dwErr = GetLastError(); 532 autostartSvcDisplayError("delete - DeleteService failed, rc=%Rrc (%#x)\n", RTErrConvertFromWin32(dwErr), dwErr); 533 } 527 534 CloseServiceHandle(hSvc); 528 535 } 529 else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) 530 { 531 532 if (g_cVerbosity) 533 RTPrintf("The service %s was not installed, nothing to be done.", sServiceName.c_str()); 534 else 535 RTPrintf("Successfully deleted the %s service.\n", sServiceName.c_str()); 536 rc = RTEXITCODE_SUCCESS; 537 } 538 return rc; 536 return rcExit; 539 537 } 540 538 … … 786 784 */ 787 785 int rc = RTSemEventMultiSignal(g_hSupSvcWinEvent); 788 if (RT_FAILURE(rc)) 789 autostartSvcLogError ("SERVICE_CONTROL_STOP: RTSemEventMultiSignal failed, %Rrc\n", rc);786 if (RT_FAILURE(rc)) /** @todo r=andy Don't we want to report back an error here to SCM? */ 787 autostartSvcLogErrorRc(rc, "SERVICE_CONTROL_STOP: RTSemEventMultiSignal failed, %Rrc\n", rc); 790 788 791 789 return NO_ERROR; … … 805 803 } 806 804 807 static RTEXITCODEautostartStartVMs(void)805 static int autostartStartVMs(void) 808 806 { 809 807 int rc = autostartSetup(); 810 808 if (RT_FAILURE(rc)) 811 return RTEXITCODE_FAILURE;809 return rc; 812 810 813 811 const char *pszConfigFile = RTEnvGet("VBOXAUTOSTART_CONFIG"); 814 812 if (!pszConfigFile) 815 return autostartSvcLogError("Starting VMs failed. VBOXAUTOSTART_CONFIG environment variable is not defined.\n"); 813 return autostartSvcLogErrorRc(VERR_ENV_VAR_NOT_FOUND, 814 "Starting VMs failed. VBOXAUTOSTART_CONFIG environment variable is not defined.\n"); 816 815 bool fAllow = false; 817 816 … … 819 818 rc = autostartParseConfig(pszConfigFile, &pCfgAst); 820 819 if (RT_FAILURE(rc)) 821 return autostartSvcLogError("Starting VMs failed. Failed to parse the config file. Check the access permissions and file structure.\n"); 822 820 return autostartSvcLogErrorRc(rc, "Starting VMs failed. Failed to parse the config file. Check the access permissions and file structure.\n"); 823 821 PCFGAST pCfgAstPolicy = autostartConfigAstGetByName(pCfgAst, "default_policy"); 824 822 /* Check default policy. */ … … 835 833 { 836 834 autostartConfigAstDestroy(pCfgAst); 837 return autostartSvcLogError ("'default_policy' must be either 'allow' or 'deny'.\n");835 return autostartSvcLogErrorRc(VERR_INVALID_PARAMETER, "'default_policy' must be either 'allow' or 'deny'.\n"); 838 836 } 839 837 } … … 844 842 { 845 843 autostartConfigAstDestroy(pCfgAst); 846 return autostartSvcLogError ("Failed to query username of the process (%Rrc).\n", rc);844 return autostartSvcLogErrorRc(rc, "Failed to query username of the process (%Rrc).\n", rc); 847 845 } 848 846 … … 877 875 { 878 876 autostartConfigAstDestroy(pCfgAst); 879 return autostartSvcLogError ("'allow' must be either 'true' or 'false'.\n");877 return autostartSvcLogErrorRc(VERR_INVALID_PARAMETER, "'allow' must be either 'true' or 'false'.\n"); 880 878 } 881 879 } … … 884 882 { 885 883 autostartConfigAstDestroy(pCfgAst); 886 return autostartSvcLogError ("Invalid config, user is not a compound node.\n");884 return autostartSvcLogErrorRc(VERR_INVALID_PARAMETER, "Invalid config, user is not a compound node.\n"); 887 885 } 888 886 … … 890 888 { 891 889 autostartConfigAstDestroy(pCfgAst); 892 return autostartSvcLogError("User is not allowed to autostart VMs.\n"); 893 } 894 895 RTEXITCODE rcExit = autostartStartMain(pCfgAstUser); 890 return autostartSvcLogErrorRc(VERR_INVALID_PARAMETER, "User is not allowed to autostart VMs.\n"); 891 } 892 893 if (RT_SUCCESS(rc)) 894 rc = autostartStartMain(pCfgAstUser); 895 896 896 autostartConfigAstDestroy(pCfgAst); 897 if (rcExit != RTEXITCODE_SUCCESS) 898 autostartSvcLogError("Starting VMs failed\n"); 899 900 return rcExit; 897 898 return rc; 901 899 } 902 900 … … 953 951 { 954 952 /* No one signaled us to stop */ 955 RTEXITCODE ec = autostartStartVMs(); 956 if (ec == RTEXITCODE_SUCCESS) 957 { 958 LogFlow(("autostartSvcWinServiceMain: done starting VMs\n")); 959 dwErr = NO_ERROR; 960 } 961 /* No reason to keep started. Shutdown the service*/ 953 rc = autostartStartVMs(); 962 954 } 963 955 autostartShutdown(); … … 982 974 autostartSvcWinSetServiceStatus(SERVICE_STOPPED, 0, dwErr); 983 975 } 984 else 985 { 986 dwErr = GetLastError(); 987 autostartSvcLogError("RegisterServiceCtrlHandlerEx failed, rc=%Rrc (%#x)\n", RTErrConvertFromWin32(dwErr), dwErr); 988 } 989 990 LogFlowFuncLeave(); 976 /* else error will be handled by the caller. */ 991 977 } 992 978 … … 1001 987 static RTEXITCODE autostartSvcWinRunIt(int argc, char **argv) 1002 988 { 1003 int rc;989 int vrc; 1004 990 1005 991 LogFlowFuncEnter(); … … 1031 1017 { 1032 1018 char szLogFile[RTPATH_MAX]; 1033 rc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile),1019 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile), 1034 1020 /* :fCreateDir */ false); 1035 if (RT_FAILURE( rc))1036 { 1037 autostartSvcLogError("Failed to get VirtualBox user home directory: %Rrc\n", rc);1021 if (RT_FAILURE(vrc)) 1022 { 1023 autostartSvcLogError("Failed to get VirtualBox user home directory: %Rrc\n", vrc); 1038 1024 break; 1039 1025 } … … 1045 1031 } 1046 1032 1047 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxAutostart.log");1048 if (RT_FAILURE( rc))1049 { 1050 autostartSvcLogError( "Failed to construct release log file name: %Rrc\n",rc);1051 break; 1052 } 1053 1054 rc = com::VBoxLogRelCreate(AUTOSTART_SERVICE_NAME,1033 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxAutostart.log"); 1034 if (RT_FAILURE(vrc)) 1035 { 1036 autostartSvcLogError( "Failed to construct release log file name: %Rrc\n", vrc); 1037 break; 1038 } 1039 1040 vrc = com::VBoxLogRelCreate(AUTOSTART_SERVICE_NAME, 1055 1041 szLogFile, 1056 1042 RTLOGFLAGS_PREFIX_THREAD … … 1064 1050 g_uHistoryFileSize, 1065 1051 NULL); 1066 if (RT_FAILURE( rc))1067 autostartSvcLogError("Failed to create release log file: %Rrc\n", rc);1052 if (RT_FAILURE(vrc)) 1053 autostartSvcLogError("Failed to create release log file: %Rrc\n", vrc); 1068 1054 } while (0); 1069 1055 … … 1112 1098 { 1113 1099 autostartSvcLogError("runit failed, service name is missing"); 1114 return RTEXITCODE_FAILURE; 1115 } 1116 1100 return RTEXITCODE_SYNTAX; 1101 } 1117 1102 1118 1103 autostartSvcLogInfo("Starting service %ls\n", g_bstrServiceName.raw()); … … 1127 1112 { NULL, NULL} 1128 1113 }; 1114 1129 1115 if (StartServiceCtrlDispatcherW(&s_aServiceStartTable[0])) 1130 1116 { … … 1137 1123 { 1138 1124 case ERROR_FAILED_SERVICE_CONTROLLER_CONNECT: 1139 autostartSvcWinServiceMain(0, NULL);//autostartSvcDisplayError("Cannot run a service from the command line. Use the 'start' action to start it the right way.\n"); 1125 autostartSvcLogWarning("Cannot run a service from the command line. Use the 'start' action to start it the right way.\n"); 1126 autostartSvcWinServiceMain(0 /* cArgs */, NULL /* papwszArgs */); 1140 1127 break; 1141 1128 default: … … 1147 1134 1148 1135 return RTEXITCODE_FAILURE; 1149 }1150 1151 1152 static void autostartSvcShowHeader(void)1153 {1154 RTPrintf(VBOX_PRODUCT " VirtualBox Autostart Service Version " VBOX_VERSION_STRING " - r%s\n"1155 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"1156 "All rights reserved.\n\n", RTBldCfgRevisionStr());1157 1136 } 1158 1137 … … 1187 1166 * Do the printing. 1188 1167 */ 1189 if (fBrief) 1190 RTPrintf("%s\n", VBOX_VERSION_STRING); 1191 else 1192 autostartSvcShowHeader(); 1168 autostartSvcShowVersion(fBrief); 1169 1193 1170 return RTEXITCODE_SUCCESS; 1194 1171 } … … 1211 1188 "\n" 1212 1189 "Global options:\n" 1213 " <help|-?|-h|--help> [...]\n" 1214 " Displays this help screen.\n" 1215 " <version|-v|--version> [-brief]\n" 1216 " Displays the version.\n" 1217 "\n" 1190 " -v\n" 1191 " Increases the verbosity. Can be specified multiple times." 1192 "\n\n" 1218 1193 "No command given:\n" 1219 1194 " Runs the service.\n" 1195 "Options:\n" 1220 1196 " --service <name>\n" 1221 1197 " Specifies the service name to run.\n" 1198 "\n" 1199 "Command </help|help|-?|-h|--help> [...]\n" 1200 " Displays this help screen.\n" 1201 "\n" 1202 "Command </version|version|-V|--version> [-brief]\n" 1203 " Displays the version.\n" 1222 1204 "\n" 1223 1205 "Command </i|install|/RegServer> --user <username> --password-file <...>\n" … … 1225 1207 "Options:\n" 1226 1208 " --user <username>\n" 1227 " Specifies the user name the service should use for installation.\n"1209 " Specifies the user name the service should be installed for.\n" 1228 1210 " --password-file <path/to/file>\n" 1229 1211 " Specifies the file for user password to use for installation.\n" 1230 1212 "\n" 1231 1213 "Command </u|uninstall|delete|/UnregServer>\n" 1232 " Uninstalls the service.\n", 1214 " Uninstalls the service.\n" 1215 " --user <username>\n" 1216 " Specifies the user name the service should will be deleted for.\n", 1233 1217 pszExe); 1234 1218 return RTEXITCODE_SUCCESS; … … 1324 1308 return autostartSvcWinShowHelp(); 1325 1309 else if ( !stricmp(argv[iArg], "version") 1326 || !stricmp(argv[iArg], "/v ")1327 || !stricmp(argv[iArg], "- v")1310 || !stricmp(argv[iArg], "/ver") 1311 || !stricmp(argv[iArg], "-V") /* Note: "-v" is used for specifying the verbosity. */ 1328 1312 || !stricmp(argv[iArg], "/version") 1329 1313 || !stricmp(argv[iArg], "-version") -
trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostart.h
r95103 r95139 126 126 127 127 /** 128 * Prints the service header header (product name, version, ++) to stdout. 129 */ 130 DECLHIDDEN(void) autostartSvcShowHeader(void); 131 132 /** 133 * Prints the service version information header to stdout. 134 * 135 * @param fBrief Whether to show brief information or not. 136 */ 137 DECLHIDDEN(void) autostartSvcShowVersion(bool fBrief); 138 139 /** 128 140 * Log messages to the system and release log. 129 141 * … … 181 193 * Main routine for the autostart daemon. 182 194 * 183 * @returns exitstatus code.195 * @returns VBox status code. 184 196 * @param pCfgAst Config AST for the startup part of the autostart daemon. 185 197 */ 186 DECLHIDDEN( RTEXITCODE) autostartStartMain(PCFGAST pCfgAst);198 DECLHIDDEN(int) autostartStartMain(PCFGAST pCfgAst); 187 199 188 200 /** … … 190 202 * during system shutdown. 191 203 * 192 * @returns exitstatus code.204 * @returns VBox status code. 193 205 * @param pCfgAst Config AST for the shutdown part of the autostart daemon. 194 206 */ 195 DECLHIDDEN( RTEXITCODE) autostartStopMain(PCFGAST pCfgAst);196 197 /** 198 * Logs a verbose message to the appropriate system log .207 DECLHIDDEN(int) autostartStopMain(PCFGAST pCfgAst); 208 209 /** 210 * Logs a verbose message to the appropriate system log and stdout + release log (if configured). 199 211 * 200 212 * @param cVerbosity Verbosity level when logging should happen. … … 205 217 206 218 /** 207 * Logs a verbose message to the appropriate system log .219 * Logs a verbose message to the appropriate system log and stdout + release log (if configured). 208 220 * 209 221 * @param cVerbosity Verbosity level when logging should happen. … … 214 226 215 227 /** 216 * Logs a warning message to the appropriate system log .228 * Logs a warning message to the appropriate system log and stdout + release log (if configured). 217 229 * 218 230 * @param pszFormat The log string. No trailing newline. … … 222 234 223 235 /** 224 * Logs a warning message to the appropriate system log .236 * Logs a warning message to the appropriate system log and stdout + release log (if configured). 225 237 * 226 238 * @param pszFormat The log string. No trailing newline. … … 230 242 231 243 /** 232 * Logs a info message to the appropriate system log .244 * Logs a info message to the appropriate system log and stdout + release log (if configured). 233 245 * 234 246 * @param pszFormat The log string. No trailing newline. … … 238 250 239 251 /** 240 * Logs a info message to the appropriate system log .252 * Logs a info message to the appropriate system log and stdout + release log (if configured). 241 253 * 242 254 * @param pszFormat The log string. No trailing newline. … … 246 258 247 259 /** 260 * Logs the message to the appropriate system log and stderr + release log (if configured). 261 * 262 * In debug builds this will also put it in the debug log. 263 * 264 * @returns VBox status code. 265 * @param pszFormat The log string. No trailing newline. 266 * @param ... Format arguments. 267 */ 268 DECLHIDDEN(int) autostartSvcLogErrorV(const char *pszFormat, va_list va); 269 270 /** 271 * Logs the message to the appropriate system log and stderr + release log (if configured). 272 * 273 * In debug builds this will also put it in the debug log. 274 * 275 * @returns VBox status code. 276 * @param pszFormat The log string. No trailing newline. 277 * @param ... Format arguments. 278 */ 279 DECLHIDDEN(int) autostartSvcLogError(const char *pszFormat, ...); 280 281 /** 248 282 * Logs the message to the appropriate system log. 249 283 * 250 284 * In debug builds this will also put it in the debug log. 251 285 * 252 * @param pszFormat The log string. No trailing newline. 253 * @param ... Format arguments. 254 * 255 * @todo This should later be replaced by the release logger and callback destination(s). 256 */ 257 DECLHIDDEN(RTEXITCODE) autostartSvcLogErrorV(const char *pszFormat, va_list va); 286 * @returns VBox status code specified by \a rc. 287 * @param pszFormat The log string. No trailing newline. 288 * @param ... Format arguments. 289 * 290 * @note Convenience function to return directly with the specified \a rc. 291 */ 292 DECLHIDDEN(int) autostartSvcLogErrorRcV(int rc, const char *pszFormat, va_list va); 258 293 259 294 /** … … 262 297 * In debug builds this will also put it in the debug log. 263 298 * 264 * @param pszFormat The log string. No trailing newline. 265 * @param ... Format arguments. 266 * 267 * @todo This should later be replaced by the release logger and callback destination(s). 268 */ 269 DECLHIDDEN(RTEXITCODE) autostartSvcLogError(const char *pszFormat, ...); 299 * @returns VBox status code specified by \a rc. 300 * @param pszFormat The log string. No trailing newline. 301 * @param ... Format arguments. 302 * 303 * @note Convenience function to return directly with the specified \a rc. 304 */ 305 DECLHIDDEN(int) autostartSvcLogErrorRc(int rc, const char *pszFormat, ...); 270 306 271 307 /** 272 308 * Deals with RTGetOpt failure, bitching in the system log. 273 309 * 274 * @returns 1310 * @returns VBox status code specified by \a rc. 275 311 * @param pszAction The action name. 276 312 * @param rc The RTGetOpt return value. … … 280 316 * @param pValue The value returned by RTGetOpt. 281 317 */ 282 DECLHIDDEN( RTEXITCODE) autostartSvcLogGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTGETOPTUNION pValue);318 DECLHIDDEN(int) autostartSvcLogGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTGETOPTUNION pValue); 283 319 284 320 /** 285 321 * Bitch about too many arguments (after RTGetOpt stops) in the system log. 286 322 * 287 * @returns 1323 * @returns VERR_INVALID_PARAMETER 288 324 * @param pszAction The action name. 289 325 * @param argc The argument count. … … 291 327 * @param iArg The argument index. 292 328 */ 293 DECLHIDDEN( RTEXITCODE) autostartSvcLogTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg);329 DECLHIDDEN(int) autostartSvcLogTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg); 294 330 295 331 /** 296 332 * Prints an error message to the screen. 297 333 * 334 * @returns RTEXITCODE 298 335 * @param pszFormat The message format string. 299 336 * @param va Format arguments. … … 304 341 * Prints an error message to the screen. 305 342 * 343 * @returns RTEXITCODE 306 344 * @param pszFormat The message format string. 307 345 * @param ... Format arguments. … … 319 357 DECLHIDDEN(RTEXITCODE) autostartSvcDisplayGetOptError(const char *pszAction, int rc, PCRTGETOPTUNION pValue); 320 358 321 DECLHIDDEN(int) autostartSetup( );322 323 DECLHIDDEN(void) autostartShutdown( );359 DECLHIDDEN(int) autostartSetup(void); 360 361 DECLHIDDEN(void) autostartShutdown(void); 324 362 325 363 #endif /* !VBOX_INCLUDED_SRC_VBoxAutostart_VBoxAutostart_h */ -
trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostartCfg.cpp
r93115 r95139 456 456 * Log unexpected token error. 457 457 * 458 * @returns nothing.458 * @returns VBox status code (VERR_INVALID_PARAMETER). 459 459 * @param pToken The token which caused the error. 460 460 * @param pszExpected String of the token which was expected. 461 461 */ 462 static voidautostartConfigTokenizerMsgUnexpectedToken(PCFGTOKEN pToken, const char *pszExpected)463 { 464 autostartSvcLogError("Unexpected token '%s' at %d:%d.%d, expected '%s'",465 autostartConfigTokenToString(pToken),466 pToken->iLine, pToken->cchStart,467 pToken->cchStart + autostartConfigTokenGetLength(pToken) - 1, pszExpected);462 static int autostartConfigTokenizerMsgUnexpectedToken(PCFGTOKEN pToken, const char *pszExpected) 463 { 464 return autostartSvcLogErrorRc(VERR_INVALID_PARAMETER, "Unexpected token '%s' at %d:%d.%d, expected '%s'", 465 autostartConfigTokenToString(pToken), 466 pToken->iLine, pToken->cchStart, 467 pToken->cchStart + autostartConfigTokenGetLength(pToken) - 1, pszExpected); 468 468 } 469 469 … … 484 484 { 485 485 if (pCfgToken->enmType != enmType) 486 { 487 autostartConfigTokenizerMsgUnexpectedToken(pCfgToken, autostartConfigTokenTypeToStr(enmType)); 488 rc = VERR_INVALID_PARAMETER; 489 } 486 return autostartConfigTokenizerMsgUnexpectedToken(pCfgToken, autostartConfigTokenTypeToStr(enmType)); 490 487 491 488 autostartConfigTokenFree(pCfgTokenizer, pCfgToken); … … 573 570 } 574 571 else 575 { 576 autostartConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token"); 577 rc = VERR_INVALID_PARAMETER; 578 } 572 rc = autostartConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token"); 579 573 580 574 return rc; … … 641 635 } 642 636 else if (RT_SUCCESS(rc)) 643 { 644 autostartConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token"); 645 rc = VERR_INVALID_PARAMETER; 646 } 637 rc = autostartConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token"); 647 638 648 639 /* Add to the current compound node. */ … … 724 715 } 725 716 case CFGASTNODETYPE_LIST: 717 RT_FALL_THROUGH(); 726 718 default: 727 719 AssertMsgFailed(("Invalid AST node type %d\n", pCfgAst->enmType)); -
trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStart.cpp
r95103 r95139 54 54 } 55 55 56 DECLHIDDEN( RTEXITCODE) autostartStartMain(PCFGAST pCfgAst)56 DECLHIDDEN(int) autostartStartMain(PCFGAST pCfgAst) 57 57 { 58 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;59 58 int vrc = VINF_SUCCESS; 60 59 std::list<AUTOSTARTVM> listVM; 61 60 uint32_t uStartupDelay = 0; 61 62 autostartSvcLogVerbose(1, "Starting machines ...\n"); 62 63 63 64 pCfgAst = autostartConfigAstGetByName(pCfgAst, "startup_delay"); … … 68 69 vrc = RTStrToUInt32Full(pCfgAst->u.KeyValue.aszValue, 10, &uStartupDelay); 69 70 if (RT_FAILURE(vrc)) 70 return RTMsgErrorExit(RTEXITCODE_FAILURE, "'startup_delay' must be an unsigned number");71 return autostartSvcLogErrorRc(vrc, "'startup_delay' must be an unsigned number"); 71 72 } 72 73 } … … 79 80 80 81 if (vrc == VERR_INTERRUPTED) 81 return RTEXITCODE_SUCCESS;82 return VINF_SUCCESS; 82 83 83 84 /* … … 96 97 if (machines[i]) 97 98 { 99 Bstr strName; 100 CHECK_ERROR_BREAK(machines[i], COMGETTER(Name)(strName.asOutParam())); 101 98 102 BOOL fAccessible; 99 103 CHECK_ERROR_BREAK(machines[i], COMGETTER(Accessible)(&fAccessible)); 100 104 if (!fAccessible) 105 { 106 autostartSvcLogVerbose(1, "Machine '%ls' is not accessible, skipping\n", strName.raw()); 101 107 continue; 108 } 109 110 AUTOSTARTVM autostartVM; 102 111 103 112 BOOL fAutostart; … … 105 114 if (fAutostart) 106 115 { 107 AUTOSTARTVM autostartVM;108 109 116 CHECK_ERROR_BREAK(machines[i], COMGETTER(Id)(autostartVM.strId.asOutParam())); 110 117 CHECK_ERROR_BREAK(machines[i], COMGETTER(AutostartDelay)(&autostartVM.uStartupDelay)); … … 112 119 listVM.push_back(autostartVM); 113 120 } 121 122 autostartSvcLogVerbose(1, "Machine '%ls': Autostart is %s (startup delay is %RU32 seconds)\n", 123 strName.raw(), fAutostart ? "enabled" : "disabled", 124 fAutostart ? autostartVM.uStartupDelay : 0); 114 125 } 115 126 } … … 120 131 * just to add this log, hence a bit of duplicate logic here. 121 132 */ 122 if (SUCCEEDED(rc) && listVM.empty()) 123 LogRel(("No VMs configured for autostart\n")); 133 if (SUCCEEDED(rc)) 134 { 135 if (machines.size() == 0) 136 autostartSvcLogWarning("No virtual machines found.\n" 137 "This either could be a configuration problem (access rights), " 138 "or there are no VMs configured yet."); 139 else if (listVM.empty()) 140 autostartSvcLogWarning("No virtual machines configured for autostart.\n" 141 "Please consult the manual about how to enable auto starting VMs.\n"); 142 } 143 else 144 autostartSvcLogError("Enumerating virtual machines failed with %Rhrc\n", rc); 124 145 125 146 if ( SUCCEEDED(rc) 126 147 && !listVM.empty()) 127 148 { 128 ULONG uDelayCur r= 0;149 ULONG uDelayCur = 0; 129 150 130 151 /* Sort by startup delay and apply base override. */ … … 137 158 ComPtr<IProgress> progress; 138 159 139 if ((*it).uStartupDelay > uDelayCurr) 140 { 141 autostartSvcLogVerbose(1, "Delaying start of the next VMs for %ul seconds ...\n", 142 (*it).uStartupDelay - uDelayCurr); 143 RTThreadSleep(((*it).uStartupDelay - uDelayCurr) * 1000); 144 uDelayCurr = (*it).uStartupDelay; 145 } 146 147 CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine((*it).strId.raw(), 148 machine.asOutParam())); 160 CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine((*it).strId.raw(), machine.asOutParam())); 161 162 Bstr strName; 163 CHECK_ERROR_BREAK(machine, COMGETTER(Name)(strName.asOutParam())); 164 165 if ((*it).uStartupDelay > uDelayCur) 166 { 167 autostartSvcLogVerbose(1, "Waiting for %ul seconds before starting machine '%s' ...\n", 168 (*it).uStartupDelay - uDelayCur, strName.raw()); 169 RTThreadSleep(((*it).uStartupDelay - uDelayCur) * 1000); 170 uDelayCur = (*it).uStartupDelay; 171 } 149 172 150 173 CHECK_ERROR_BREAK(machine, LaunchVMProcess(g_pSession, Bstr("headless").raw(), … … 152 175 if (SUCCEEDED(rc) && !progress.isNull()) 153 176 { 154 autostartSvcLogVerbose(1, "Waiting for VM '%ls' to power on...\n", (*it).strId.raw());177 autostartSvcLogVerbose(1, "Waiting for machine '%ls' to power on ...\n", strName.raw()); 155 178 CHECK_ERROR(progress, WaitForCompletion(-1)); 156 179 if (SUCCEEDED(rc)) … … 172 195 } 173 196 else 174 autostartSvcLogVerbose(1, " VM '%ls' has been successfully started.\n", (*it).strId.raw());197 autostartSvcLogVerbose(1, "Machine '%ls' has been successfully started.\n", strName.raw()); 175 198 } 176 199 } … … 185 208 } 186 209 187 return rcExit;210 return vrc; 188 211 } 189 212 -
trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStop.cpp
r93115 r95139 16 16 */ 17 17 18 #include <iprt/assert.h> 19 #include <iprt/log.h> 20 #include <iprt/message.h> 21 #include <iprt/stream.h> 22 #include <iprt/thread.h> 23 #include <iprt/time.h> 24 18 25 #include <VBox/com/com.h> 19 26 #include <VBox/com/string.h> … … 22 29 #include <VBox/com/ErrorInfo.h> 23 30 #include <VBox/com/errorprint.h> 24 25 #include <iprt/thread.h>26 #include <iprt/stream.h>27 #include <iprt/log.h>28 #include <iprt/assert.h>29 #include <iprt/message.h>30 31 31 32 #include <list> … … 103 104 } 104 105 105 DECLHIDDEN( RTEXITCODE) autostartStopMain(PCFGAST pCfgAst)106 DECLHIDDEN(int) autostartStopMain(PCFGAST pCfgAst) 106 107 { 107 108 RT_NOREF(pCfgAst); 108 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;109 109 std::list<AUTOSTOPVM> listVM; 110 111 autostartSvcLogVerbose(1, "Stopping machines ...\n"); 110 112 111 113 /* … … 125 127 if (machines[i]) 126 128 { 129 Bstr strName; 130 CHECK_ERROR_BREAK(machines[i], COMGETTER(Name)(strName.asOutParam())); 131 127 132 BOOL fAccessible; 128 133 CHECK_ERROR_BREAK(machines[i], COMGETTER(Accessible)(&fAccessible)); 129 134 if (!fAccessible) 135 { 136 autostartSvcLogVerbose(1, "Machine '%ls' is not accessible, skipping\n", strName.raw()); 130 137 continue; 138 } 139 140 AUTOSTOPVM autostopVM; 131 141 132 142 AutostopType_T enmAutostopType; … … 134 144 if (enmAutostopType != AutostopType_Disabled) 135 145 { 136 AUTOSTOPVM autostopVM;137 138 146 CHECK_ERROR_BREAK(machines[i], COMGETTER(Id)(autostopVM.strId.asOutParam())); 139 147 autostopVM.enmAutostopType = enmAutostopType; … … 141 149 listVM.push_back(autostopVM); 142 150 } 151 152 autostartSvcLogVerbose(1, "Machine '%ls': Autostop type is %#x\n", 153 strName.raw(), autostopVM.enmAutostopType); 143 154 } 144 155 } … … 155 166 CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine((*it).strId.raw(), 156 167 machine.asOutParam())); 168 169 Bstr strName; 170 CHECK_ERROR_BREAK(machine, COMGETTER(Name)(strName.asOutParam())); 157 171 158 172 CHECK_ERROR_BREAK(machine, COMGETTER(State)(&enmMachineState)); … … 191 205 192 206 rc = showProgress(progress); 193 CHECK_PROGRESS_ERROR(progress, ("Failed to power off machine")); 207 CHECK_PROGRESS_ERROR(progress, ("Failed to powering off machine '%ls'", strName.raw())); 208 if (FAILED(rc)) 209 autostartSvcLogError("Powering off machine '%ls' failed with %Rhrc\n", strName.raw(), rc); 194 210 break; 195 211 } … … 198 214 BOOL fGuestEnteredACPI = false; 199 215 CHECK_ERROR_BREAK(console, GetGuestEnteredACPIMode(&fGuestEnteredACPI)); 200 if (fGuestEnteredACPI && enmMachineState == MachineState_Running) 216 if ( fGuestEnteredACPI 217 && enmMachineState == MachineState_Running) 201 218 { 202 219 CHECK_ERROR_BREAK(console, PowerButton()); 203 220 204 autostartSvcLogInfo("Waiting for VM \"%ls\" to power off...\n", (*it).strId.raw()); 205 206 do 221 autostartSvcLogVerbose(1, "Waiting for machine '%ls' to power off...\n", strName.raw()); 222 223 uint64_t const tsStartMs = RTTimeMilliTS(); 224 RTMSINTERVAL const msTimeout = RT_MS_5MIN; /* Should be enough time, shouldn't it? */ 225 226 while (RTTimeMilliTS() - tsStartMs <= msTimeout) 207 227 { 208 RTThreadSleep(1000);209 228 CHECK_ERROR_BREAK(machine, COMGETTER(State)(&enmMachineState)); 210 } while (enmMachineState == MachineState_Running); 229 if (enmMachineState != MachineState_Running) 230 break; 231 RTThreadSleep(RT_MS_1SEC); 232 } 233 234 if (RTTimeMilliTS() - tsStartMs > msTimeout) 235 autostartSvcLogWarning("Machine '%ls' did not power off via ACPI within time\n", strName.raw()); 211 236 } 212 237 else 213 238 { 214 239 /* Use save state instead and log this to the console. */ 215 autostartSvcLogWarning("The guest of VM \"%ls\"does not support ACPI shutdown or is currently paused, saving state...\n",216 (*it).strId.raw());240 autostartSvcLogWarning("The guest of machine '%ls' does not support ACPI shutdown or is currently paused, saving state...\n", 241 strName.raw()); 217 242 rc = autostartSaveVMState(console); 218 243 } … … 220 245 } 221 246 default: 222 autostartSvcLogWarning("Unknown autostop type for VM \"%ls\"\n", (*it).strId.raw());247 autostartSvcLogWarning("Unknown autostop type for machine '%ls', skipping\n", strName.raw()); 223 248 } 224 249 g_pSession->UnlockMachine(); … … 228 253 } 229 254 230 return rcExit;255 return VINF_SUCCESS; /** @todo r=andy Report back the overall status here. */ 231 256 } 232 257 -
trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostartUtils.cpp
r95103 r95139 25 25 26 26 #include <VBox/err.h> 27 27 #include <VBox/version.h> 28 29 #include <iprt/buildconfig.h> 28 30 #include <iprt/message.h> 29 31 #include <iprt/thread.h> … … 94 96 } 95 97 96 DECLHIDDEN(RTEXITCODE) autostartSvcLogErrorV(const char *pszFormat, va_list va) 97 { 98 if (*pszFormat) 99 { 100 char *pszMsg = NULL; 101 if (RTStrAPrintfV(&pszMsg, pszFormat, va) != -1) 102 { 103 autostartSvcOsLogStr(pszMsg, AUTOSTARTLOGTYPE_ERROR); 104 RTStrFree(pszMsg); 105 } 106 else 107 autostartSvcOsLogStr(pszFormat, AUTOSTARTLOGTYPE_ERROR); 108 } 109 return RTEXITCODE_FAILURE; 110 } 111 112 DECLHIDDEN(RTEXITCODE) autostartSvcLogError(const char *pszFormat, ...) 113 { 114 va_list va; 115 va_start(va, pszFormat); 116 autostartSvcLogErrorV(pszFormat, va); 117 va_end(va); 118 return RTEXITCODE_FAILURE; 98 DECLHIDDEN(void) autostartSvcShowHeader(void) 99 { 100 RTPrintf(VBOX_PRODUCT " VirtualBox Autostart Service Version " VBOX_VERSION_STRING " - r%s\n" 101 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n" 102 "All rights reserved.\n\n", RTBldCfgRevisionStr()); 103 } 104 105 DECLHIDDEN(void) autostartSvcShowVersion(bool fBrief) 106 { 107 if (fBrief) 108 RTPrintf("%s\n", VBOX_VERSION_STRING); 109 else 110 autostartSvcShowHeader(); 111 } 112 113 DECLHIDDEN(int) autostartSvcLogErrorV(const char *pszFormat, va_list va) 114 { 115 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER); 116 117 char *pszMsg = NULL; 118 if (RTStrAPrintfV(&pszMsg, pszFormat, va) != -1) 119 { 120 autostartSvcOsLogStr(pszMsg, AUTOSTARTLOGTYPE_ERROR); 121 RTStrFree(pszMsg); 122 return VINF_SUCCESS; 123 } 124 125 return VERR_BUFFER_OVERFLOW; 126 } 127 128 DECLHIDDEN(int) autostartSvcLogError(const char *pszFormat, ...) 129 { 130 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER); 131 132 va_list va; 133 va_start(va, pszFormat); 134 int rc = autostartSvcLogErrorV(pszFormat, va); 135 va_end(va); 136 137 return rc; 138 } 139 140 DECLHIDDEN(int) autostartSvcLogErrorRcV(int rc, const char *pszFormat, va_list va) 141 { 142 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER); 143 144 int rc2 = autostartSvcLogErrorV(pszFormat, va); 145 if (RT_SUCCESS(rc2)) 146 return rc; /* Return handed-in rc. */ 147 return rc2; 148 } 149 150 DECLHIDDEN(int) autostartSvcLogErrorRc(int rc, const char *pszFormat, ...) 151 { 152 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER); 153 154 va_list va; 155 va_start(va, pszFormat); 156 int rc2 = autostartSvcLogErrorRcV(rc, pszFormat, va); 157 va_end(va); 158 return rc2; 119 159 } 120 160 … … 129 169 if (RTStrAPrintfV(&pszMsg, pszFormat, va) != -1) 130 170 { 131 RTPrintf("%s", pszMsg);132 171 autostartSvcOsLogStr(pszMsg, AUTOSTARTLOGTYPE_VERBOSE); 133 172 RTStrFree(pszMsg); … … 178 217 if (RTStrAPrintfV(&pszMsg, pszFormat, va) != -1) 179 218 { 180 RTPrintf("%s", pszMsg);181 219 autostartSvcOsLogStr(pszMsg, AUTOSTARTLOGTYPE_INFO); 182 220 RTStrFree(pszMsg); … … 184 222 } 185 223 186 DECLHIDDEN(RTEXITCODE) autostartSvcLogGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTGETOPTUNION pValue) 187 { 188 NOREF(pValue); 189 autostartSvcLogError("%s - RTGetOpt failure, %Rrc (%d): %s", 190 pszAction, rc, rc, iArg < argc ? argv[iArg] : "<null>"); 191 return RTEXITCODE_FAILURE; 192 } 193 194 DECLHIDDEN(RTEXITCODE) autostartSvcLogTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg) 195 { 196 Assert(iArg < argc); 224 DECLHIDDEN(int) autostartSvcLogGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTGETOPTUNION pValue) 225 { 226 RT_NOREF(pValue); 227 autostartSvcLogError("%s - RTGetOpt failure, %Rrc (%d): %s", pszAction, rc, rc, iArg < argc ? argv[iArg] : "<null>"); 228 return RTEXITCODE_SYNTAX; 229 } 230 231 DECLHIDDEN(int) autostartSvcLogTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg) 232 { 233 AssertReturn(iArg < argc, RTEXITCODE_FAILURE); 197 234 autostartSvcLogError("%s - Too many arguments: %s", pszAction, argv[iArg]); 198 235 for ( ; iArg < argc; iArg++) 199 236 LogRel(("arg#%i: %s\n", iArg, argv[iArg])); 200 return RTEXITCODE_FAILURE;237 return VERR_INVALID_PARAMETER; 201 238 } 202 239
Note:
See TracChangeset
for help on using the changeset viewer.