Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1138 lines
25 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. ntinitss.c
  5. Abstract:
  6. This module contains the code to establish the connection between
  7. the session console process and the PSX Emulation Subsystem.
  8. Author:
  9. Avi Nathan (avin) 17-Jul-1991
  10. Environment:
  11. User Mode Only
  12. Revision History:
  13. Ellen Aycock-Wright (ellena) 15-Sept-1991 Modified for POSIX
  14. --*/
  15. #include <stdlib.h>
  16. #include <stdio.h>
  17. #include <string.h>
  18. #include <signal.h>
  19. #include <errno.h>
  20. #include <io.h>
  21. #define _POSIX_
  22. #include <limits.h>
  23. #define NTPSX_ONLY
  24. #include "psxses.h"
  25. #include <ntsm.h>
  26. #include "sesport.h"
  27. #define COPY_TO_SESSION_DATABASE(pchEnvp) { \
  28. *ppch++ = TmpPtr - (ULONG_PTR)Buf; \
  29. (void)strcpy(TmpPtr, pchEnvp); \
  30. TmpPtr += strlen(TmpPtr); \
  31. *TmpPtr++ = '\0'; } \
  32. #define MyFree(x) if(NULL!=x)free(x);
  33. const int iDaysInMonths[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
  34. //
  35. // PSXSES protocol with PSXSS
  36. //
  37. // Connect
  38. // 1. PSXSES ----------> PSXSS
  39. //
  40. //
  41. // Connect
  42. // 2. PSXSES <---------- PSXSS
  43. //
  44. //
  45. // SesConCreate
  46. // 3. PSXSES ----------> PSXSS (StartProcess)
  47. //
  48. //
  49. // PSXSES connects to the PSXSS, then PSXSS connects back. Then PSXSES
  50. // sends the Create
  51. //
  52. //
  53. /*
  54. * prototypes for internal functions.
  55. */
  56. PCHAR BuildTZEnvVar();
  57. PCHAR ConvertTimeFromBias( LONG );
  58. int MakeJulianDate( PTIME_FIELDS );
  59. PCHAR MakeTime( PTIME_FIELDS );
  60. PCHAR MakeTZName( WCHAR * );
  61. BOOL CreateConsoleDataSection(VOID);
  62. PCHAR ConvertPathVar(PCHAR);
  63. int
  64. CountOpenFiles()
  65. /*++
  66. CountOpenFiles -- return the number of file descriptors in
  67. use.
  68. --*/
  69. {
  70. int i;
  71. i = _dup(0);
  72. if (-1 == i) {
  73. if (EBADF == errno) {
  74. return 0;
  75. }
  76. if (EMFILE == errno) {
  77. return _NFILE;
  78. }
  79. // what other error?
  80. return 3;
  81. }
  82. _close(i);
  83. return i;
  84. }
  85. DWORD
  86. InitPsxSessionPort(
  87. VOID
  88. )
  89. {
  90. char PortName[PSX_SES_BASE_PORT_NAME_LENGTH];
  91. STRING PsxSessionPortName;
  92. UNICODE_STRING PsxSessionPortName_U;
  93. UNICODE_STRING PsxSSPortName;
  94. HANDLE RequestThread;
  95. DWORD dwThreadId;
  96. NTSTATUS Status;
  97. OBJECT_ATTRIBUTES ObjectAttributes;
  98. ULONG ConnectionInfoLen;
  99. PSXSESCONNECTINFO ConnectionInfo;
  100. SECURITY_QUALITY_OF_SERVICE DynamicQos;
  101. BOOLEAN DeferedPosixLoadAttempted = FALSE;
  102. if (!CreateConsoleDataSection()) {
  103. return(0);
  104. }
  105. //
  106. // Get a private ID for the session port.
  107. //
  108. PSX_GET_SESSION_PORT_NAME((char *)&PortName, GetSessionUniqueId());
  109. RtlInitAnsiString(&PsxSessionPortName, PortName);
  110. //
  111. // Create session port
  112. //
  113. Status = RtlAnsiStringToUnicodeString(&PsxSessionPortName_U,
  114. &PsxSessionPortName, TRUE);
  115. if (!NT_SUCCESS(Status)) {
  116. PsxSessionPort = NULL;
  117. return 0;
  118. }
  119. InitializeObjectAttributes(&ObjectAttributes, &PsxSessionPortName_U, 0,
  120. NULL, NULL);
  121. Status = NtCreatePort(&PsxSessionPort, &ObjectAttributes,
  122. sizeof(SCCONNECTINFO), sizeof(SCREQUESTMSG),
  123. 4096 * 16);
  124. RtlFreeUnicodeString(&PsxSessionPortName_U);
  125. if (!NT_SUCCESS(Status)) {
  126. PsxSessionPort = NULL;
  127. return 0;
  128. }
  129. //
  130. // Create a thread to handle requests to the session port including
  131. // connection requests.
  132. //
  133. RequestThread = CreateThread( NULL,
  134. 0,
  135. ServeSessionRequests,
  136. NULL,
  137. 0,
  138. &dwThreadId
  139. );
  140. if (RequestThread == NULL) {
  141. NtClose( PsxSessionPort );
  142. return 0;
  143. }
  144. SetThreadPriority(RequestThread, THREAD_PRIORITY_ABOVE_NORMAL);
  145. //
  146. // connect to PSXSS and notify of the new session and the port associated
  147. // with it. It will connected back to the session port just created.
  148. //
  149. ConnectionInfo.In.SessionUniqueId = GetSessionUniqueId();
  150. ConnectionInfoLen = sizeof(ConnectionInfo);
  151. PSX_GET_SESSION_OBJECT_NAME(&PsxSSPortName, PSX_SS_SESSION_PORT_NAME);
  152. //
  153. // Set up the security quality of service parameters to use over the
  154. // port. Use the most efficient (least overhead) - which is dynamic
  155. // rather than static tracking.
  156. //
  157. DynamicQos.ImpersonationLevel = SecurityImpersonation;
  158. DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  159. DynamicQos.EffectiveOnly = TRUE;
  160. retry_connect:
  161. Status = NtConnectPort(&PsxSSPortHandle, &PsxSSPortName, &DynamicQos,
  162. NULL, NULL, NULL, (PVOID)&ConnectionInfo,
  163. &ConnectionInfoLen);
  164. if (!NT_SUCCESS(Status)) {
  165. if ( DeferedPosixLoadAttempted == FALSE ) {
  166. HANDLE SmPort;
  167. UNICODE_STRING PosixName;
  168. DeferedPosixLoadAttempted = TRUE;
  169. Status = SmConnectToSm(NULL,NULL,0,&SmPort);
  170. if ( NT_SUCCESS(Status) ) {
  171. RtlInitUnicodeString(&PosixName,L"POSIX");
  172. SmLoadDeferedSubsystem(SmPort,&PosixName);
  173. goto retry_connect;
  174. }
  175. }
  176. KdPrint(("PSXSES: Unable to connect to %ws: %X\n",
  177. PsxSSPortName.Buffer, Status));
  178. return 0;
  179. }
  180. return HandleToUlong(ConnectionInfo.Out.SessionPortHandle);
  181. }
  182. VOID
  183. ScHandleConnectionRequest(
  184. IN PSCREQUESTMSG Message
  185. )
  186. {
  187. NTSTATUS Status;
  188. PSCCONNECTINFO ConnectionInfo = &Message->ConnectionRequest;
  189. HANDLE CommPortHandle;
  190. PORT_VIEW ServerView;
  191. // BUGBUG: Add verification test
  192. if (FALSE) {
  193. // Reject
  194. Status = NtAcceptConnectPort(&CommPortHandle, NULL,
  195. (PPORT_MESSAGE) Message, FALSE, NULL, NULL);
  196. } else {
  197. // ??? Any reply
  198. ConnectionInfo->dummy = 0;
  199. // BUGBUG:
  200. ServerView.Length = sizeof(ServerView);
  201. ServerView.SectionOffset = 0L;
  202. ServerView.ViewSize = 0L;
  203. Status = NtAcceptConnectPort(&CommPortHandle, NULL,
  204. (PPORT_MESSAGE) Message, TRUE, NULL, NULL);
  205. if (!NT_SUCCESS(Status)) {
  206. KdPrint(("PSXSES: Accept failed: %X\n",
  207. Status));
  208. exit(1);
  209. }
  210. //
  211. // Record the view section address in a
  212. // global variable.
  213. //
  214. // BUGBUG: PsxSesConPortBaseAddress =
  215. // ServerView.ViewBase;
  216. Status = NtCompleteConnectPort(CommPortHandle);
  217. ASSERT(NT_SUCCESS(Status));
  218. {
  219. PCLIENT_AND_PORT pc;
  220. pc = malloc(sizeof(*pc));
  221. if (NULL == pc) {
  222. return;
  223. }
  224. pc->ClientId = Message->h.ClientId;
  225. pc->CommPort = CommPortHandle;
  226. InsertTailList(&ClientPortsList, &pc->Links);
  227. }
  228. }
  229. }
  230. //
  231. // create a section to be shared by all client processes running in this
  232. // session. returns a pointer to the base.
  233. //
  234. BOOL
  235. CreateConsoleDataSection(
  236. VOID
  237. )
  238. {
  239. char SessionName[PSX_SES_BASE_PORT_NAME_LENGTH];
  240. STRING PsxSessionDataName;
  241. UNICODE_STRING PsxSessionDataName_U;
  242. NTSTATUS Status;
  243. OBJECT_ATTRIBUTES ObjectAttributes;
  244. HANDLE SectionHandle;
  245. LARGE_INTEGER SectionSize;
  246. SIZE_T ViewSize=0L;
  247. BOOLEAN DeferedPosixLoadAttempted = FALSE;
  248. //
  249. // Get a private ID for the session data.
  250. //
  251. PSX_GET_SESSION_DATA_NAME((char *)&SessionName, GetSessionUniqueId());
  252. RtlInitAnsiString(&PsxSessionDataName, SessionName);
  253. Status = RtlAnsiStringToUnicodeString(&PsxSessionDataName_U,
  254. &PsxSessionDataName, TRUE);
  255. if (!NT_SUCCESS(Status)) {
  256. KdPrint(("PSXSES: RtlAnsiStringToUnicode (%s) failed: 0x%x\n",
  257. &SessionName,
  258. Status));
  259. return FALSE;
  260. }
  261. InitializeObjectAttributes(&ObjectAttributes, &PsxSessionDataName_U, 0,
  262. NULL, NULL);
  263. //
  264. // create a 64k section.
  265. // BUGBUG: cruiser apis allow io of more then 64k
  266. //
  267. retry_create:
  268. SectionSize.LowPart = PSX_SESSION_PORT_MEMORY_SIZE;
  269. SectionSize.HighPart = 0L;
  270. Status = NtCreateSection(&SectionHandle,
  271. SECTION_MAP_WRITE,
  272. &ObjectAttributes, &SectionSize, PAGE_READWRITE,
  273. SEC_COMMIT, NULL);
  274. if (!NT_SUCCESS(Status)) {
  275. if ( DeferedPosixLoadAttempted == FALSE ) {
  276. HANDLE SmPort;
  277. UNICODE_STRING PosixName;
  278. LARGE_INTEGER TimeOut;
  279. DeferedPosixLoadAttempted = TRUE;
  280. Status = SmConnectToSm(NULL,NULL,0,&SmPort);
  281. if ( NT_SUCCESS(Status) ) {
  282. RtlInitUnicodeString(&PosixName,L"POSIX");
  283. SmLoadDeferedSubsystem(SmPort,&PosixName);
  284. //
  285. // Sleep for 1 seconds
  286. //
  287. TimeOut.QuadPart = (LONGLONG)1000 * (LONGLONG)-10000;
  288. NtDelayExecution(FALSE,&TimeOut);
  289. goto retry_create;
  290. }
  291. }
  292. RtlFreeUnicodeString(&PsxSessionDataName_U);
  293. KdPrint(("PSXSES: NtCreateSection (%wZ) failed: 0x%x\n",
  294. &PsxSessionDataName_U, Status));
  295. return FALSE;
  296. }
  297. RtlFreeUnicodeString(&PsxSessionDataName_U);
  298. //
  299. // Map the the whole section to virtual address.
  300. // Let MM locate the view.
  301. //
  302. PsxSessionDataBase = 0L;
  303. Status = NtMapViewOfSection(SectionHandle, NtCurrentProcess(),
  304. &PsxSessionDataBase, 0L, 0L, NULL,
  305. &ViewSize, ViewUnmap, 0L, PAGE_READWRITE);
  306. if (!NT_SUCCESS(Status)) {
  307. ASSERT(NT_SUCCESS(Status));
  308. return FALSE;
  309. }
  310. PsxSessionDataSectionHandle = SectionHandle;
  311. return(TRUE);
  312. }
  313. BOOL
  314. StartProcess(
  315. DWORD SessionPortHandle,
  316. char *PgmName,
  317. char *CurrentDir,
  318. int argc,
  319. char **argv,
  320. char **envp
  321. )
  322. /*++
  323. Description:
  324. Start the POSIX process running by calling PsxSesConCreate. We use
  325. the port memory to pass the name of the command, its args, and the
  326. environment strings. The layout of the port memory looks like this:
  327. PsxSessionDataBase:
  328. Command line, nul-terminated.
  329. Buff:
  330. argv[0]
  331. argv[1]
  332. ...
  333. NULL
  334. environ[0]
  335. environ[1]
  336. ...
  337. NULL
  338. <argv strings>
  339. <environ strings>
  340. Since we'll be passing this to the server process, we make all the
  341. pointers relative to Buff.
  342. --*/
  343. {
  344. PSXSESREQUESTMSG RequestMsg;
  345. PSXSESREQUESTMSG ReplyMsg;
  346. PSCREQ_CREATE Create;
  347. NTSTATUS Status;
  348. static char path[256];
  349. char *Buf;
  350. char *TmpPtr;
  351. int i;
  352. char **ppch;
  353. char *pch;
  354. char *pchHomeDrive; // pointer to HOMEDRIVE environ var
  355. char *pchHomePath; // pointer to HOMEPATH environ var
  356. BOOLEAN fSuccess;
  357. UNICODE_STRING DosPath_U, Path_U;
  358. ANSI_STRING DosPath_A, Path_A;
  359. //
  360. // Set Header info
  361. //
  362. PORT_MSG_DATA_LENGTH(RequestMsg) = sizeof(RequestMsg) -
  363. sizeof(PORT_MESSAGE);
  364. // BUGBUG: too much
  365. PORT_MSG_TOTAL_LENGTH(RequestMsg) = sizeof(RequestMsg);
  366. PORT_MSG_ZERO_INIT(RequestMsg) = 0L;
  367. //
  368. // Set request info
  369. //
  370. // BUGBUG: use common memory
  371. RequestMsg.Request = SesConCreate;
  372. Create = &RequestMsg.d.Create;
  373. Create->SessionUniqueId = GetSessionUniqueId();
  374. Create->SessionPort = (HANDLE)SessionPortHandle;
  375. Create->Buffer = PsxSessionDataBase;
  376. Create->OpenFiles = CountOpenFiles();
  377. Buf = PsxSessionDataBase;
  378. Create->PgmNameOffset = 0;
  379. RtlInitAnsiString(&DosPath_A, PgmName);
  380. Status = RtlAnsiStringToUnicodeString(&DosPath_U, &DosPath_A, TRUE);
  381. fSuccess = RtlDosPathNameToNtPathName_U(
  382. DosPath_U.Buffer,
  383. &Path_U,
  384. NULL,
  385. NULL
  386. );
  387. RtlFreeUnicodeString(&DosPath_U);
  388. if (!fSuccess) {
  389. return fSuccess;
  390. }
  391. Status = RtlUnicodeStringToAnsiString(&Path_A, &Path_U, TRUE);
  392. if (!NT_SUCCESS(Status)) {
  393. RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)Path_U.Buffer);
  394. return FALSE;
  395. }
  396. strcpy(Buf, Path_A.Buffer);
  397. RtlFreeAnsiString(&Path_A);
  398. RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)Path_U.Buffer);
  399. Buf += strlen(Buf) + 1;
  400. if ((ULONG_PTR)Buf & 0x1)
  401. ++Buf;
  402. if ((ULONG_PTR)Buf & 0x2)
  403. Buf += 2;
  404. Create->CurrentDirOffset = PtrToUlong(Buf - (ULONG_PTR)PsxSessionDataBase);
  405. strcpy(Buf, CurrentDir);
  406. Buf += strlen(Buf) + 1;
  407. if ((ULONG_PTR)Buf & 0x1)
  408. ++Buf;
  409. if ((ULONG_PTR)Buf & 0x2)
  410. Buf += 2;
  411. Create->ArgsOffset = PtrToUlong(Buf - (ULONG_PTR)PsxSessionDataBase);
  412. //
  413. // We need to know how much space to leave for the argv and environ
  414. // vectors to know where to start the strings. We have argc, which
  415. // was passed in, but we need to count the environ strings.
  416. //
  417. argc++; // for trailing argv null
  418. for (ppch = envp; NULL != *ppch; ++ppch)
  419. ++argc;
  420. ++argc; // for trailing environ null
  421. ++argc; // for LOGNAME
  422. ++argc; // for HOME
  423. ++argc; // for TZ
  424. ++argc; // for _PSXLIBPATH
  425. argc += 1;
  426. //
  427. // TmpPtr indicates the place at which the argument and environment
  428. // strings will be placed.
  429. //
  430. TmpPtr = &Buf[argc * sizeof(char *)];
  431. // copy the argv pointers and strings
  432. ppch = (char **)&Buf[0];
  433. while (NULL != *argv) {
  434. *ppch++ = TmpPtr - (ULONG_PTR)Buf;
  435. (void)strcpy(TmpPtr, *argv++);
  436. TmpPtr += strlen(TmpPtr);
  437. *TmpPtr++ = '\0';
  438. }
  439. *ppch++ = NULL;
  440. Create->EnvironmentOffset = PtrToUlong(TmpPtr - (ULONG_PTR)PsxSessionDataBase);
  441. pchHomeDrive = NULL;
  442. pchHomePath = NULL;
  443. while (NULL != *envp) {
  444. char *p;
  445. int bPathFound = 0;
  446. //
  447. // Upcase the variable part of each environment string.
  448. //
  449. if (NULL == (p = strchr(*envp, '='))) {
  450. // no equals sign? Skip this one.
  451. *envp++;
  452. continue;
  453. }
  454. *p = '\0';
  455. _strupr(*envp);
  456. if (0 == strcmp(*envp, "PATH") && !bPathFound) {
  457. bPathFound = 1;
  458. // Save PATH as LIBPATH.
  459. pch = malloc(strlen(p + 1) + sizeof("_PSXLIBPATH") + 2);
  460. if (NULL != pch) {
  461. strcpy(pch, "_PSXLIBPATH=");
  462. strcat(pch, p + 1);
  463. COPY_TO_SESSION_DATABASE(pch);
  464. free(pch);
  465. }
  466. // Convert PATH to POSIX-format
  467. *p = '=';
  468. *envp = ConvertPathVar(*envp);
  469. if (NULL == *envp) {
  470. // no memory; skip the path
  471. *envp++;
  472. continue;
  473. }
  474. } else if (0 == strcmp(*envp, "USERNAME")) {
  475. // Copy USERNAME to LOGNAME
  476. *p = '=';
  477. pch = malloc(strlen(*envp));
  478. if (NULL == pch) {
  479. continue;
  480. }
  481. sprintf(pch,"LOGNAME=%s",strchr(*envp,'=')+1);
  482. COPY_TO_SESSION_DATABASE(pch);
  483. free(pch);
  484. } else if (0 == strcmp(*envp, "HOMEPATH")) {
  485. pchHomePath = p+1;
  486. *p = '=';
  487. } else if (0 == strcmp(*envp, "HOMEDRIVE")) {
  488. pchHomeDrive = p+1;
  489. *p = '=';
  490. } else {
  491. *p = '=';
  492. }
  493. COPY_TO_SESSION_DATABASE(*envp);
  494. *envp++;
  495. }
  496. //
  497. // setup the TZ env var
  498. //
  499. pch = BuildTZEnvVar();
  500. if (NULL != pch) {
  501. COPY_TO_SESSION_DATABASE(pch);
  502. free(pch);
  503. }
  504. //
  505. // if the HOMEPATH env var was not set, we'll have to set HOME to //C/
  506. //
  507. if (NULL == pchHomePath || NULL == pchHomeDrive) {
  508. COPY_TO_SESSION_DATABASE("HOME=//C/");
  509. } else {
  510. char *pch2;
  511. pch = malloc((5 + strlen(pchHomeDrive)+strlen(pchHomePath))*2);
  512. if (NULL != pch) {
  513. sprintf(pch,"HOME=%s%s",pchHomeDrive,pchHomePath);
  514. pch2 = ConvertPathVar(pch);
  515. free(pch);
  516. if (NULL != pch2) {
  517. COPY_TO_SESSION_DATABASE(pch2);
  518. }
  519. }
  520. }
  521. *ppch = NULL;
  522. //
  523. // for all request pass the session handle except for the CREATE request
  524. // where no session have been allocated yet.
  525. //
  526. RequestMsg.Session = NULL;
  527. Status = NtRequestWaitReplyPort(PsxSSPortHandle, (PPORT_MESSAGE)&RequestMsg,
  528. (PPORT_MESSAGE)&ReplyMsg);
  529. if (!NT_SUCCESS(Status)) {
  530. KdPrint(("PSXSES: Unable to exec process: %X\n", Status));
  531. return FALSE;
  532. }
  533. ASSERT(PORT_MSG_TYPE(ReplyMsg) == LPC_REPLY);
  534. //
  535. // get the session handle for the newly created session.
  536. //
  537. SSSessionHandle = ReplyMsg.d.Create.SessionPort;
  538. return(NT_SUCCESS(ReplyMsg.Status));
  539. }
  540. VOID
  541. TerminateSession(
  542. IN ULONG ExitStatus
  543. )
  544. {
  545. NTSTATUS Status;
  546. //
  547. // remove event handler so we don't try to send a message
  548. // for a dying session.
  549. //
  550. SetEventHandlers(FALSE);
  551. // Close the named objects: port, section
  552. Status = NtClose(PsxSSPortHandle);
  553. ASSERT(NT_SUCCESS(Status));
  554. Status = NtClose(PsxSessionDataSectionHandle);
  555. ASSERT(NT_SUCCESS(Status));
  556. // notify PSXSS
  557. // Cleanup TaskMan stuff
  558. _exit(ExitStatus);
  559. }
  560. #define CTRL_CLOSE_EVENT 2
  561. #define CTRL_LOGOFF_EVENT 5
  562. #define CTRL_SHUTDOWN_EVENT 6
  563. BOOL
  564. EventHandlerRoutine(
  565. IN DWORD CtrlType
  566. )
  567. {
  568. int SignalType;
  569. BOOL r;
  570. #if 0
  571. //
  572. // These events are now handled -mjb.
  573. //
  574. if (CTRL_CLOSE_EVENT == CtrlType) {
  575. return FALSE; // not handled
  576. }
  577. if (CTRL_LOGOFF_EVENT == CtrlType) {
  578. return FALSE; // not handled
  579. }
  580. if (CTRL_SHUTDOWN_EVENT == CtrlType) {
  581. return FALSE; // not handled
  582. }
  583. #endif
  584. switch (CtrlType) {
  585. case CTRL_C_EVENT:
  586. r = TRUE;
  587. SignalType = PSX_SIGINT;
  588. break;
  589. case CTRL_BREAK_EVENT:
  590. r = TRUE;
  591. SignalType = PSX_SIGQUIT;
  592. break;
  593. default:
  594. r = FALSE;
  595. SignalType = PSX_SIGKILL;
  596. break;
  597. }
  598. SignalSession(SignalType);
  599. //
  600. // return TRUE to avoid having the default handler called and
  601. // the process exited for us (the signal may be ignored or handled,
  602. // after all).
  603. //
  604. // return FALSE to be exited immediately.
  605. //
  606. return r;
  607. }
  608. PCHAR
  609. ConvertPathVar(PCHAR opath)
  610. /*++
  611. pch is malloced with twice the size of opath because the new path is
  612. going to be longer than opath because of drive letter conversions
  613. but will ALWAYS be less than strlen(opath)*2 chars.
  614. --*/
  615. {
  616. char *pch, *p, *q;
  617. pch = malloc(strlen(opath) * 2);
  618. if (NULL == pch) {
  619. return NULL;
  620. }
  621. p = pch;
  622. while ('\0' != *opath) {
  623. if (';' == *opath) {
  624. // semis become colons
  625. *p++ = ':';
  626. } else if ('\\' == *opath) {
  627. // back-slashes become slashes
  628. *p++ = '/';
  629. } else if (':' == *(opath + 1)) {
  630. // "X:" becomes "//X"
  631. *p++ = '/';
  632. *p++ = '/';
  633. // the drive letters must be uppercase.
  634. *p++ = (char)toupper(*opath);
  635. ++opath; // skip the colon
  636. } else {
  637. *p++ = *opath;
  638. }
  639. ++opath;
  640. }
  641. *p = '\0';
  642. return pch;
  643. }
  644. PCHAR
  645. BuildTZEnvVar(
  646. )
  647. /*++
  648. Routine Description:
  649. This routine allocates a buffer and formats the TZ environment
  650. variable for Posix applications. The returned buffer is of the
  651. form:
  652. TZ=stdoffset[dst[offset][,start[/time],end[/time]]]
  653. See IEEE Std 1003.1 page 152 for more information.
  654. Arguments:
  655. None.
  656. Return Value:
  657. Pointer to a buffer containing the formatted TZ variable.
  658. --*/
  659. {
  660. RTL_TIME_ZONE_INFORMATION TZInfo;
  661. NTSTATUS Status;
  662. int iTmp;
  663. PCHAR pcRet;
  664. PCHAR pcOffStr1, pcOffStr2;
  665. PCHAR pcTimeStr1, pcTimeStr2;
  666. PCHAR pcStandardName, pcDaylightName;
  667. BOOL fDstSpecified;
  668. pcOffStr1 = pcOffStr2 = NULL;
  669. pcTimeStr1 = pcTimeStr2 = NULL;
  670. pcStandardName = pcDaylightName = NULL;
  671. pcRet = malloc( 60 + 2 * TZNAME_MAX ); // Conservative guess of max
  672. if( NULL == pcRet ) {
  673. return( NULL );
  674. }
  675. Status = RtlQueryTimeZoneInformation( &TZInfo );
  676. if( !( NT_SUCCESS( Status ) ) ) {
  677. free( pcRet );
  678. return( NULL );
  679. }
  680. pcStandardName = MakeTZName( TZInfo.StandardName );
  681. if (NULL == pcStandardName)
  682. goto out;
  683. pcOffStr1 = ConvertTimeFromBias( TZInfo.Bias + TZInfo.StandardBias );
  684. if (NULL == pcOffStr1)
  685. goto out;
  686. //
  687. // If DaylightStart.Month is 0, the date is not specified.
  688. //
  689. if (TZInfo.DaylightStart.Month != 0) {
  690. pcDaylightName = MakeTZName( TZInfo.DaylightName );
  691. pcTimeStr1 = MakeTime( &TZInfo.DaylightStart );
  692. if (NULL == pcTimeStr1)
  693. goto out;
  694. pcTimeStr2 = MakeTime( &TZInfo.StandardStart );
  695. if (NULL == pcTimeStr2)
  696. goto out;
  697. pcOffStr2 = ConvertTimeFromBias( TZInfo.Bias + TZInfo.DaylightBias );
  698. if (NULL == pcOffStr2)
  699. goto out;
  700. fDstSpecified = TRUE;
  701. } else {
  702. fDstSpecified = FALSE;
  703. }
  704. if (fDstSpecified) {
  705. if (TZInfo.DaylightStart.Year == 0) {
  706. sprintf(pcRet, "TZ=%s%s%s%s,M%d.%d.%d/%s,M%d.%d.%d/%s",
  707. pcStandardName, pcOffStr1,
  708. pcDaylightName, pcOffStr2,
  709. TZInfo.DaylightStart.Month,
  710. TZInfo.DaylightStart.Day,
  711. TZInfo.DaylightStart.Weekday,
  712. pcTimeStr1,
  713. TZInfo.StandardStart.Month,
  714. TZInfo.StandardStart.Day,
  715. TZInfo.StandardStart.Weekday,
  716. pcTimeStr2);
  717. } else {
  718. sprintf(pcRet, "TZ=%s%s%s%s,J%d/%s,J%d/%s",
  719. pcStandardName, pcOffStr1,
  720. pcDaylightName, pcOffStr2,
  721. MakeJulianDate(&TZInfo.DaylightStart),
  722. pcTimeStr1,
  723. MakeJulianDate(&TZInfo.StandardStart),
  724. pcTimeStr2);
  725. }
  726. } else {
  727. sprintf(pcRet, "TZ=%s%s", pcStandardName, pcOffStr1);
  728. }
  729. out:
  730. MyFree(pcOffStr1);
  731. MyFree(pcOffStr2);
  732. MyFree(pcTimeStr1);
  733. MyFree(pcTimeStr2);
  734. MyFree(pcStandardName);
  735. MyFree(pcDaylightName);
  736. return pcRet;
  737. }
  738. PCHAR
  739. MakeTZName(
  740. IN WCHAR * FullName
  741. )
  742. /*++
  743. Routine Description:
  744. This routine formats a time zone name string from a full
  745. name description of a time zone. The return string is the
  746. first letter of each word in the input string, or TZNAME_MAX
  747. chars from the full name if there are fewer than three words.
  748. The returned buffer should be freed by the caller.
  749. Arguments:
  750. FullName - pointer to the full name description of a time zone
  751. Return Value:
  752. Pointer to a buffer containing the formatted time zone name.
  753. --*/
  754. {
  755. PWCHAR Rover;
  756. PCHAR cRet;
  757. PCHAR DestRover;
  758. BOOL GrabNext;
  759. int GrabCount;
  760. GrabNext = TRUE;
  761. GrabCount = 0;
  762. cRet = malloc( TZNAME_MAX+1 );
  763. if( NULL == cRet ) {
  764. return( NULL );
  765. }
  766. DestRover = cRet;
  767. for( Rover = FullName; *Rover != L'\0'; Rover++ ) {
  768. if( GrabNext ) {
  769. if( *Rover == L' ' ) {
  770. continue;
  771. }
  772. wctomb( DestRover, *Rover );
  773. *DestRover++ = (CHAR)toupper( *DestRover );
  774. if( ( ++GrabCount ) == TZNAME_MAX ) {
  775. break;
  776. }
  777. GrabNext = FALSE;
  778. } else {
  779. if( *Rover == L' ' ) {
  780. GrabNext = TRUE;
  781. }
  782. }
  783. }
  784. if( GrabCount < 3 ) {
  785. wcstombs( cRet, FullName, TZNAME_MAX );
  786. cRet[ TZNAME_MAX ] = '\0';
  787. } else {
  788. *DestRover = '\0';
  789. }
  790. return( cRet );
  791. }
  792. int
  793. MakeJulianDate(
  794. IN PTIME_FIELDS tm
  795. )
  796. /*++
  797. Routine Description:
  798. This routine calculates Julian day n (1<=n<=365). Leap years are
  799. not counted so Feb 29 is not representable.
  800. Arguments:
  801. x - pointer to TIME_FIELDS structure to use as the input date
  802. Return Value:
  803. Julian day.
  804. --*/
  805. {
  806. int Idx;
  807. int iJDate;
  808. for( Idx = 0, iJDate = tm->Day;
  809. Idx < tm->Month - 1; ++Idx ) {
  810. iJDate += iDaysInMonths[Idx];
  811. }
  812. if( tm->Month == 2 && tm->Day == 29 ) {
  813. --iJDate;
  814. }
  815. return( iJDate );
  816. }
  817. PCHAR
  818. ConvertTimeFromBias(
  819. IN LONG Bias
  820. )
  821. /*++
  822. Routine Description:
  823. This routine allocates a buffer, and formats a time string in the
  824. buffer as hh:mm:ss or hh:mm or hh:ss where hh may be only 1 digit.
  825. The time may be negative. The buffer should be freed by the caller.
  826. Arguments:
  827. x - pointer to a LONG representing the number of minutes of time
  828. Return Value:
  829. Pointer to a buffer with the time string.
  830. --*/
  831. {
  832. PCHAR cRet;
  833. int iHours, iMins, iSecs;
  834. cRet = malloc( 21 );
  835. if( NULL == cRet ) {
  836. return( NULL );
  837. }
  838. iHours = Bias / 60;
  839. iMins = iSecs = 0;
  840. if( Bias % 60 != 0 ) {
  841. iMins = Bias / ( 60 * 60 );
  842. if( Bias % ( 60 * 60 ) != 0 ) {
  843. iSecs = Bias / ( 60 * 60 * 60 );
  844. }
  845. }
  846. if( iSecs != 0 ) {
  847. sprintf( cRet, "%d:%02d:%02d", iHours, iMins, iSecs );
  848. } else if( iMins != 0 ) {
  849. sprintf( cRet, "%d:%02d", iHours, iMins );
  850. } else {
  851. sprintf( cRet, "%d", iHours );
  852. }
  853. return( cRet );
  854. }
  855. PCHAR
  856. MakeTime(
  857. IN PTIME_FIELDS x
  858. )
  859. /*++
  860. Routine Description:
  861. This routine allocates a buffer, and formats a time string in the
  862. buffer as hh:mm:ss or hh:mm or hh:ss where hh may be only 1 digit.
  863. The buffer should be freed by the caller.
  864. Arguments:
  865. x - pointer to TIME_FIELDS structure to use as the input time
  866. Return Value:
  867. Pointer to a buffer with the time string.
  868. --*/
  869. {
  870. PCHAR cRet;
  871. cRet = malloc( 21 );
  872. if( NULL == cRet ) {
  873. return( NULL );
  874. }
  875. if( x->Second != 0 ) {
  876. sprintf( cRet, "%d:%02d:%02d", x->Hour, x->Minute, x->Second );
  877. } else if( x->Minute != 0 ) {
  878. sprintf( cRet, "%d:%02d", x->Hour, x->Minute );
  879. } else {
  880. sprintf( cRet, "%d", x->Hour );
  881. }
  882. return( cRet );
  883. }
  884. void
  885. SignalSession(
  886. int SignalType
  887. )
  888. {
  889. PSXSESREQUESTMSG RequestMsg;
  890. PSXSESREQUESTMSG ReplyMsg;
  891. NTSTATUS Status;
  892. //
  893. // Set Header info
  894. //
  895. PORT_MSG_DATA_LENGTH(RequestMsg) = sizeof(RequestMsg) -
  896. sizeof(PORT_MESSAGE);
  897. PORT_MSG_TOTAL_LENGTH(RequestMsg) = sizeof(RequestMsg);
  898. // BUGBUG: too much
  899. PORT_MSG_ZERO_INIT(RequestMsg) = 0L;
  900. RequestMsg.Request = SesConSignal;
  901. RequestMsg.Session = SSSessionHandle;
  902. RequestMsg.UniqueId = GetSessionUniqueId();
  903. RequestMsg.d.Signal.Type = SignalType;
  904. Status = NtRequestWaitReplyPort(PsxSSPortHandle,
  905. (PPORT_MESSAGE)&RequestMsg, (PPORT_MESSAGE)&ReplyMsg);
  906. if (!NT_SUCCESS(Status)) {
  907. KdPrint(("PSXSES: Unable to send signal: %X\n", Status));
  908. ExitProcess(1);
  909. }
  910. if (LPC_REPLY != PORT_MSG_TYPE(ReplyMsg)) {
  911. KdPrint(("PSXSES: unexpected MSG_TYPE %d\n", PORT_MSG_TYPE(ReplyMsg)));
  912. ExitProcess(1);
  913. }
  914. }