ODKrnl.c 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675
  1. /* OpenDoors Online Software Programming Toolkit
  2. * (C) Copyright 1991 - 1999 by Brian Pirie.
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. *
  18. *
  19. * File: ODKrnl.c
  20. *
  21. * Description: Contains the OpenDoors kernel, which is responsible for many
  22. * of the core functions which continue regardless of what the
  23. * client program is doing. The implementation of this file is
  24. * central to the OpenDoors architecture. The functionality
  25. * implemented by the OpenDoors kernel includes (but is not
  26. * limited to):
  27. *
  28. * - Obtaining and input from the user, through the modem
  29. * and possibly the local keyboard.
  30. * - Monitoring maximum time and inactivity time limits.
  31. * - Responding to loss of carrier.
  32. * - Forcing the status line to be updated regularily,
  33. * on platforms that it exists.
  34. * - Implementing the system operator <-> remote user chat
  35. * mode.
  36. *
  37. * Revisions: Date Ver Who Change
  38. * ---------------------------------------------------------------
  39. * Jan 01, 1995 6.00 BP Split off from odcore.c
  40. * Nov 11, 1995 6.00 BP Removed register keyword.
  41. * Nov 14, 1995 6.00 BP Added include of odscrn.h.
  42. * Nov 15, 1995 6.00 BP 32-bit portability.
  43. * Nov 16, 1995 6.00 BP Removed oddoor.h, added odcore.h.
  44. * Nov 17, 1995 6.00 BP Use new input queue mechanism.
  45. * Nov 21, 1995 6.00 BP Ported to Win32.
  46. * Dec 12, 1995 6.00 BP Added entry, exit and kernel macros.
  47. * Dec 13, 1995 6.00 BP Moved chat mode code to ODKrnl.h.
  48. * Dec 24, 1995 6.00 BP od_chat_active = TRUE on chat start.
  49. * Dec 30, 1995 6.00 BP Added ODCALL for calling convention.
  50. * Jan 04, 1996 6.00 BP tODInQueueEvent -> tODInputEvent.
  51. * Jan 12, 1996 6.00 BP Added bOnlyShiftArrow.
  52. * Jan 30, 1996 6.00 BP Replaced od_yield() with od_sleep().
  53. * Jan 30, 1996 6.00 BP Add semaphore timeout.
  54. * Feb 06, 1996 6.00 BP Added od_silent_mode.
  55. * Feb 19, 1996 6.00 BP Changed version number to 6.00.
  56. * Feb 23, 1996 6.00 BP Only create active semapore once.
  57. * Mar 03, 1996 6.10 BP Begin version 6.10.
  58. * Mar 06, 1996 6.10 BP Prevent TC generated N_SCOPY@ call.
  59. * Mar 13, 1996 6.10 BP bOnlyShiftArrow -> nArrowUseCount.
  60. * Mar 19, 1996 6.10 BP MSVC15 source-level compatibility.
  61. * Oct 22, 2001 6.21 RS Lowered thread priorities to normal.
  62. * Aug 10, 2003 6.23 SH *nix support
  63. */
  64. #define BUILDING_OPENDOORS
  65. #include <stdio.h>
  66. #include <stdlib.h>
  67. #include <string.h>
  68. #include <ctype.h>
  69. #include <time.h>
  70. #include <limits.h>
  71. #include "OpenDoor.h"
  72. #ifdef ODPLAT_NIX
  73. #include <sys/types.h>
  74. #include <unistd.h>
  75. #include <signal.h>
  76. #include <fcntl.h>
  77. #include <errno.h>
  78. #endif
  79. #include "ODCore.h"
  80. #include "ODGen.h"
  81. #include "ODPlat.h"
  82. #include "ODCom.h"
  83. #include "ODKrnl.h"
  84. #include "ODScrn.h"
  85. #include "ODInQue.h"
  86. #include "ODInEx.h"
  87. #ifdef ODPLAT_WIN32
  88. #include "ODFrame.h"
  89. #endif /* ODPLAT_WIN32 */
  90. /* Multithreading performance tuning. */
  91. #define REMOTE_INPUT_THREAD_PRIORITY OD_PRIORITY_NORMAL /* was ABOVE_NORMAL */
  92. #define NO_CARRIER_THREAD_PRIORITY OD_PRIORITY_NORMAL /* was ABOVE_NORMAL */
  93. #define NO_CARRIER_THREAD_SLEEP_TIME 6000
  94. #define TIME_UPDATE_THREAD_PRIORITY OD_PRIORITY_NORMAL
  95. #define TIME_UPDATE_THREAD_SLEEP_TIME 3000
  96. /* Misc performance tuning. */
  97. #define STATUS_UPDATE_PERIOD 3L
  98. #define CHAT_YIELD_PERIOD 25L
  99. /* Pending command identifiers. */
  100. #define KERNEL_FUNC_CHATTOGGLE 0x0001
  101. /* Private function prototypes. */
  102. static void ODKrnlHandleReceivedChar(char chReceived, BOOL bFromRemote);
  103. static void ODKrnlTimeUpdate(void);
  104. static void ODKrnlChatCleanup(void);
  105. static void ODKrnlChatMode(void);
  106. #ifdef ODPLAT_NIX
  107. #ifdef USE_KERNEL_SIGNAL
  108. static void sig_run_kernel(int sig);
  109. static void sig_get_char(int sig);
  110. static void sig_no_carrier(int sig);
  111. #endif
  112. #endif
  113. /* Functions specific to the multithreaded implementation of the kernel. */
  114. #ifdef OD_MULTITHREADED
  115. /* Thread proceedures. */
  116. DWORD OD_THREAD_FUNC ODKrnlRemoteInputThread(void *pParam);
  117. DWORD OD_THREAD_FUNC ODKrnlNoCarrierThread(void *pParam);
  118. DWORD OD_THREAD_FUNC ODKrnlTimeUpdateThread(void *pParam);
  119. DWORD OD_THREAD_FUNC ODKrnlChatThread(void *pParam);
  120. /* Helper functions. */
  121. static void ODKrnlWaitForExclusiveControl(void);
  122. static void ODKrnlGiveUpExclusiveControl(void);
  123. #endif /* OD_MULTITHREADED */
  124. /* Local working variables. */
  125. #ifdef OD_MULTITHREADED
  126. static tODThreadHandle hRemoteInputThread = NULL;
  127. static tODThreadHandle hNoCarrierThread = NULL;
  128. static tODThreadHandle hTimeUpdateThread = NULL;
  129. static tODThreadHandle hClientThread = NULL;
  130. static tODThreadHandle hChatThread = NULL;
  131. static BOOL bHaveExclusiveControl;
  132. static BOOL bChatActivatedInternally;
  133. #endif /* OD_MULTITHREADED */
  134. static BOOL bKernelActive = FALSE;
  135. static BOOL bWarnedAboutInactivity = FALSE;
  136. static INT16 nLastInactivitySetting = 0;
  137. static time_t nNextStatusUpdateTime;
  138. static INT nKrnlFuncPending;
  139. static BOOL bLastStatusSetting;
  140. static INT16 nChatOriginalAttrib;
  141. /* Global kernel-related variables. */
  142. tODTimer RunKernelTimer;
  143. time_t nNextTimeDeductTime;
  144. char chLastControlKey = '\0';
  145. INT nArrowUseCount = 0;
  146. BOOL bForceStatusUpdate = FALSE;
  147. BOOL bIsShell;
  148. #ifdef OD_MULTITHREADED
  149. tODSemaphoreHandle hODActiveSemaphore = NULL;
  150. #endif /* OD_MULTITHREADED */
  151. /* ========================================================================= */
  152. /* Core of the OpenDoors Kernel. */
  153. /* ========================================================================= */
  154. /* ----------------------------------------------------------------------------
  155. * ODKrnlInitialize()
  156. *
  157. * Initializes kernel activities. In multithreaded versions of OpenDoors, this
  158. * is the function that starts the various kernel threads.
  159. *
  160. * Parameters: kODRCSuccess on success, or an error code on failure.
  161. *
  162. * Return: void
  163. */
  164. tODResult ODKrnlInitialize(void)
  165. {
  166. #ifdef ODPLAT_NIX
  167. sigset_t block;
  168. #ifdef USE_KERNEL_SIGNAL
  169. struct sigaction act;
  170. struct itimerval itv;
  171. #endif
  172. #endif
  173. tODResult Result = kODRCSuccess;
  174. #ifdef ODPLAT_NIX
  175. #ifdef USE_KERNEL_SIGNAL
  176. /* HUP Detection */
  177. act.sa_handler=sig_no_carrier;
  178. /* If two HUP signals are recieved, die on the second */
  179. act.sa_flags=SA_RESETHAND|SA_RESTART;
  180. sigemptyset(&(act.sa_mask));
  181. sigaction(SIGHUP,&act,NULL);
  182. /* Run kernel on SIGALRM (Every .01 seconds) */
  183. act.sa_handler=sig_run_kernel;
  184. act.sa_flags=SA_RESTART;
  185. sigemptyset(&(act.sa_mask));
  186. sigaction(SIGALRM,&act,NULL);
  187. itv.it_interval.tv_sec=0;
  188. itv.it_interval.tv_usec=10000;
  189. itv.it_value.tv_sec=0;
  190. itv.it_value.tv_usec=10000;
  191. setitimer(ITIMER_REAL,&itv,NULL);
  192. /* Make stdin signal driven. */
  193. // act.sa_handler=sig_get_char;
  194. // act.sa_flags=0;
  195. // sigemptyset(&(act.sa_mask));
  196. // sigaction(SIGIO,&act,NULL);
  197. //
  198. // /* Have SIGIO signals delivered to this process */
  199. // fcntl(0,F_SETOWN,getpid());
  200. //
  201. // /* Enable SIGIO when read possible on stdin */
  202. // fcntl(0,F_SETFL,fcntl(0,F_GETFL)|O_ASYNC);
  203. /* Make sure SIGHUP, SIGALRM, and SIGIO are unblocked */
  204. sigemptyset(&block);
  205. sigaddset(&block,SIGHUP);
  206. sigaddset(&block,SIGALRM);
  207. #if 0
  208. sigaddset(&block,SIGIO);
  209. #endif
  210. sigprocmask(SIG_UNBLOCK,&block,NULL);
  211. #else /* Using ODComCarrier... don't catch HUP signal */
  212. sigemptyset(&block);
  213. sigaddset(&block,SIGHUP);
  214. sigprocmask(SIG_BLOCK,&block,NULL);
  215. #endif
  216. #endif
  217. /* Initialize time of next status update and next time deduction. */
  218. nNextStatusUpdateTime = time(NULL) + STATUS_UPDATE_PERIOD;
  219. nNextTimeDeductTime = time(NULL) + 60L;
  220. bLastStatusSetting = od_control.od_status_on = TRUE;
  221. /* Initially, no kernel functions are pending. */
  222. nKrnlFuncPending = 0;
  223. /* Initially, the kernel is not active. */
  224. bKernelActive = FALSE;
  225. #ifdef OD_MULTITHREADED
  226. /* Initially, we do not have exclusive control of the application. */
  227. bHaveExclusiveControl = FALSE;
  228. /* Obtain a handle to the client thread. */
  229. hClientThread = ODThreadGetCurrent();
  230. /* Create OpenDoors activation semaphore. */
  231. if(hODActiveSemaphore == NULL)
  232. {
  233. Result = ODSemaphoreAlloc(&hODActiveSemaphore, 0, INT_MAX);
  234. if(Result != kODRCSuccess) return(Result);
  235. }
  236. /* Start the remote input thread if we are not operating in local mode. */
  237. if(od_control.baud != 0)
  238. {
  239. Result = ODThreadCreate(&hRemoteInputThread, ODKrnlRemoteInputThread,
  240. NULL);
  241. if(Result != kODRCSuccess) return(Result);
  242. ODThreadSetPriority(hRemoteInputThread, REMOTE_INPUT_THREAD_PRIORITY);
  243. }
  244. /* Start the carrier detection thread if we are not operating in local */
  245. /* mode. */
  246. if(od_control.baud != 0)
  247. {
  248. Result = ODThreadCreate(&hNoCarrierThread, ODKrnlNoCarrierThread, NULL);
  249. if(Result != kODRCSuccess) return(Result);
  250. ODThreadSetPriority(hNoCarrierThread, NO_CARRIER_THREAD_PRIORITY);
  251. }
  252. /* Start the time update thread. */
  253. Result = ODThreadCreate(&hTimeUpdateThread, ODKrnlTimeUpdateThread, 0);
  254. if(Result != kODRCSuccess) return(Result);
  255. ODThreadSetPriority(hTimeUpdateThread, TIME_UPDATE_THREAD_PRIORITY);
  256. #endif /* OD_MULTITHREADED */
  257. /* Return with success. */
  258. return(Result);
  259. }
  260. /* ----------------------------------------------------------------------------
  261. * ODKrnlShutdown()
  262. *
  263. * Shuts down kernel activities.
  264. *
  265. * Parameters: none
  266. *
  267. * Return: void
  268. */
  269. void ODKrnlShutdown(void)
  270. {
  271. if(bKernelActive) return;
  272. #ifdef OD_MULTITHREADED
  273. #if defined(OD_DIAGNOSTICS) && defined(ODPLAT_WIN32)
  274. if(od_control.od_internal_debug)
  275. MessageBox(NULL, "Terminating remote input thread", "OpenDoors Diagnostics", MB_OK);
  276. #endif
  277. /* Shutdown the remote input thread, if it exists. */
  278. if(hRemoteInputThread != NULL) ODThreadTerminate(hRemoteInputThread);
  279. #if defined(OD_DIAGNOSTICS) && defined(ODPLAT_WIN32)
  280. if(od_control.od_internal_debug)
  281. MessageBox(NULL, "Terminating carrier detection", "OpenDoors Diagnostics", MB_OK);
  282. #endif
  283. /* Shutdown the carrier detection thread, if it exists. */
  284. if(hNoCarrierThread != NULL) ODThreadTerminate(hNoCarrierThread);
  285. #if defined(OD_DIAGNOSTICS) && defined(ODPLAT_WIN32)
  286. if(od_control.od_internal_debug)
  287. MessageBox(NULL, "Terminating time update thread", "OpenDoors Diagnostics", MB_OK);
  288. #endif
  289. /* Shutdown the time update thread, if it exists. */
  290. if(hTimeUpdateThread != NULL) ODThreadTerminate(hTimeUpdateThread);
  291. #if defined(OD_DIAGNOSTICS) && defined(ODPLAT_WIN32)
  292. if(od_control.od_internal_debug)
  293. MessageBox(NULL, "Releasing activation semaphore", "OpenDoors Diagnostics", MB_OK);
  294. #endif
  295. #endif /* OD_MULTITHREADED */
  296. }
  297. /* ----------------------------------------------------------------------------
  298. * od_kernel()
  299. *
  300. * Carries out any kernel tasks that must be performed through regular,
  301. * explicit calls to this function,
  302. *
  303. * Parameters: none
  304. *
  305. * Return: void
  306. */
  307. ODAPIDEF void ODCALL od_kernel(void)
  308. {
  309. #ifndef OD_MULTITHREADED
  310. char ch;
  311. #ifdef ODPLAT_DOS
  312. WORD wKey;
  313. BYTE btShiftStatus;
  314. char *pszShellName;
  315. #endif
  316. BOOL bCarrier;
  317. #endif /* OD_MULTITHREADED */
  318. /* Log function entry if running in trace mode. */
  319. TRACE(TRACE_API, "od_kernel()");
  320. /* Initialize OpenDoors if not already done. */
  321. if(!bODInitialized) od_init();
  322. /* If this is an attempt at a re-entrant call to od_kernel() from another */
  323. /* function called by a currently active od_kernel(), then return without */
  324. /* doing anything. */
  325. if(bKernelActive) return;
  326. OD_API_ENTRY();
  327. /* Note that kernel is active to prevent recursive calls to the kernel. */
  328. bKernelActive = TRUE;
  329. /* Call od_ker_exec function if required. */
  330. if(od_control.od_ker_exec != NULL)
  331. {
  332. (*od_control.od_ker_exec)();
  333. }
  334. /* The remainder of od_kernel() only applies to non-multithreaded */
  335. /* versions of OpenDoors. */
  336. #ifndef OD_MULTITHREADED
  337. /* If not operating in local mode, then perform remote-mode specific */
  338. /* activies. */
  339. if(od_control.baud != 0)
  340. {
  341. #ifndef USE_KERNEL_SIGNAL
  342. /* If carrier detection is enabled, then shutdown OpenDoors if */
  343. /* the carrier detect signal is no longer high. */
  344. if(!(od_control.od_disable&DIS_CARRIERDETECT))
  345. {
  346. ODComCarrier(hSerialPort, &bCarrier);
  347. if(!bCarrier)
  348. {
  349. ODKrnlForceOpenDoorsShutdown(ERRORLEVEL_NOCARRIER);
  350. }
  351. }
  352. #endif
  353. /* Loop, obtaining any new characters from the serial port and */
  354. /* adding them to the common local/remote input queue. */
  355. while(ODComGetByte(hSerialPort, &ch, FALSE) == kODRCSuccess)
  356. {
  357. ODKrnlHandleReceivedChar(ch, TRUE);
  358. }
  359. }
  360. #ifdef ODPLAT_DOS
  361. check_keyboard_again:
  362. if(nKrnlFuncPending && !bShellChatActive)
  363. {
  364. if(nKrnlFuncPending & KERNEL_FUNC_CHATTOGGLE)
  365. {
  366. nKrnlFuncPending &=~ KERNEL_FUNC_CHATTOGGLE;
  367. goto chat_pressed;
  368. }
  369. }
  370. /* Don't check local keyboard if sysop DIS_SYSOP_KEYS is set, or if we */
  371. /* are operatingin silent mode. */
  372. if(od_control.od_disable & DIS_SYSOP_KEYS
  373. || od_control.od_silent_mode)
  374. {
  375. goto after_key_check;
  376. }
  377. ASM mov ah, 1
  378. ASM push si
  379. ASM push di
  380. ASM int 0x16
  381. ASM jnz key_waiting
  382. ASM pop di
  383. ASM pop si
  384. ASM jmp after_key_check
  385. key_waiting:
  386. ASM mov ah, 0
  387. ASM int 0x16
  388. ASM mov wKey, ax
  389. ASM mov ah, 2
  390. ASM int 0x16
  391. ASM mov btShiftStatus, al
  392. ASM pop di
  393. ASM pop si
  394. if(nArrowUseCount > 0 && (wKey == 0x4800 || wKey == 0x5000)
  395. && !(btShiftStatus & 2))
  396. {
  397. /* Pass key on to od_local_input, if it is defined. */
  398. if(od_control.od_local_input != NULL)
  399. {
  400. (*od_control.od_local_input)(wKey);
  401. }
  402. /* Add this key to the local/remote input queue. */
  403. ODKrnlHandleLocalKey(wKey);
  404. }
  405. /* If hangup key is pressed. */
  406. else if(wKey == od_control.key_hangup)
  407. {
  408. ODKrnlForceOpenDoorsShutdown(ERRORLEVEL_HANGUP);
  409. }
  410. /* If drop to BBS key is pressed. */
  411. else if(wKey == od_control.key_drop2bbs)
  412. {
  413. ODKrnlForceOpenDoorsShutdown(ERRORLEVEL_DROPTOBBS);
  414. }
  415. else if(wKey == od_control.key_dosshell)
  416. {
  417. if(!bShellChatActive)
  418. {
  419. if(pfLogWrite != NULL)
  420. (*pfLogWrite)(6);
  421. /* If function hook is defined. */
  422. if(od_control.od_cbefore_shell != NULL)
  423. {
  424. /* Then call it. */
  425. bShellChatActive = TRUE;
  426. (*od_control.od_cbefore_shell)();
  427. bShellChatActive = FALSE;
  428. }
  429. if(od_control.od_before_shell != NULL)
  430. od_disp_str(od_control.od_before_shell);
  431. if((pszShellName = (char *)getenv("COMSPEC")) == NULL)
  432. {
  433. pszShellName = (char *)"COMMAND.COM";
  434. }
  435. bIsShell = TRUE;
  436. od_spawnvpe(P_WAIT, pszShellName, NULL, NULL);
  437. bIsShell = FALSE;
  438. if(od_control.od_after_shell != NULL)
  439. od_disp_str(od_control.od_after_shell);
  440. /* If a function hook is defined. */
  441. if(od_control.od_cafter_shell != NULL)
  442. {
  443. /* Then call it. */
  444. bShellChatActive = TRUE;
  445. (*od_control.od_cafter_shell)();
  446. bShellChatActive = FALSE;
  447. }
  448. if(pfLogWrite != NULL)
  449. (*pfLogWrite)(7);
  450. }
  451. }
  452. /* If toggle chat mode key is pressed. */
  453. else if(wKey == od_control.key_chat)
  454. {
  455. chat_pressed:
  456. if(!bShellChatActive || od_control.od_chat_active)
  457. {
  458. /* If chat mode is active. */
  459. if(od_control.od_chat_active)
  460. {
  461. /* Signal exit of chat mode. */
  462. ODKrnlEndChatMode();
  463. }
  464. /* If chat mode is off. */
  465. else
  466. {
  467. /* Enable second call to kernel. */
  468. bKernelActive = FALSE;
  469. /* Enter chat mode. */
  470. ODKrnlChatMode();
  471. /* Disable second call to kernel. */
  472. bKernelActive = TRUE;
  473. }
  474. }
  475. else
  476. {
  477. if(nKrnlFuncPending & KERNEL_FUNC_CHATTOGGLE)
  478. {
  479. nKrnlFuncPending &= ~KERNEL_FUNC_CHATTOGGLE;
  480. }
  481. else
  482. {
  483. nKrnlFuncPending |= KERNEL_FUNC_CHATTOGGLE;
  484. }
  485. }
  486. }
  487. /* If sysop next key is pressed. */
  488. else if(wKey == od_control.key_sysopnext)
  489. {
  490. /* Toggle sysop next setting. */
  491. od_control.sysop_next = !od_control.sysop_next;
  492. /* Update status line. */
  493. goto statup;
  494. }
  495. /* If ESCape key is pressed and we are in chat mode. */
  496. else if((wKey&0xff) == 27 && od_control.od_chat_active)
  497. {
  498. /* Signal exit from chat mode. */
  499. od_control.od_chat_active = FALSE;
  500. }
  501. /* If lockout user key is pressed. */
  502. else if(wKey == od_control.key_lockout)
  503. {
  504. /* Set the user's access security level to 0. */
  505. od_control.user_security = 0;
  506. /* Shutdown OpenDoors. */
  507. ODKrnlForceOpenDoorsShutdown(ERRORLEVEL_HANGUP);
  508. }
  509. /* If toggle keyboard off key is pressed. */
  510. else if(wKey == od_control.key_keyboardoff)
  511. {
  512. /* Toggle user keyboard settings. */
  513. od_control.od_user_keyboard_on =! od_control.od_user_keyboard_on;
  514. /* Update status line. */
  515. goto statup;
  516. }
  517. /* If increase time key is pressed. */
  518. else if(wKey == od_control.key_moretime)
  519. {
  520. /* If time limit is less than maximum possible time limit. */
  521. if(od_control.user_timelimit < 1440)
  522. {
  523. /* Increase time left online. */
  524. ++od_control.user_timelimit;
  525. }
  526. /* Update status line. */
  527. goto statup;
  528. }
  529. /* If decrease time key is pressed. */
  530. else if(wKey == od_control.key_lesstime)
  531. {
  532. /* Never let user's time limit be set to a negative value. */
  533. if(od_control.user_timelimit > 0)
  534. {
  535. /* Decrease user's timelimit. */
  536. --od_control.user_timelimit;
  537. }
  538. /* Update the status line. */
  539. goto statup;
  540. }
  541. else
  542. {
  543. for(ch = 0; ch < 9; ++ch)
  544. {
  545. if(wKey == od_control.key_status[ch])
  546. {
  547. if(btCurrentStatusLine != ch && od_control.od_status_on)
  548. {
  549. od_set_statusline(ch);
  550. }
  551. goto check_keyboard_again;
  552. }
  553. }
  554. /* Look for user-defined hotkeys. */
  555. for(ch=0; ch<od_control.od_num_keys; ++ch)
  556. {
  557. /* If it matches. */
  558. if(wKey == (WORD)od_control.od_hot_key[ch])
  559. {
  560. /* Record keypress. */
  561. od_control.od_last_hot = wKey;
  562. /* Notify the current personality. */
  563. (*pfCurrentPersonality)(21);
  564. /* Check for a hotkey function. */
  565. if(od_control.od_hot_function[ch] != NULL)
  566. {
  567. /* Call it if it exists. */
  568. (*od_control.od_hot_function[ch])();
  569. }
  570. /* Stop searching. */
  571. break;
  572. }
  573. }
  574. /* If no hotkeys found. */
  575. if(ch >= od_control.od_num_keys)
  576. {
  577. /* Pass key on to od_local_input, if it is defined. */
  578. if(od_control.od_local_input != NULL)
  579. {
  580. (*od_control.od_local_input)(wKey);
  581. }
  582. /* Add this key to the local/remote input queue. */
  583. ODKrnlHandleLocalKey(wKey);
  584. }
  585. }
  586. goto check_keyboard_again;
  587. after_key_check:
  588. /* If status line has been turned on since last call to kernel. */
  589. if(bLastStatusSetting != od_control.od_status_on)
  590. {
  591. /* Generate the status line. */
  592. od_set_statusline(0);
  593. }
  594. bLastStatusSetting = od_control.od_status_on;
  595. if(od_control.od_update_status_now)
  596. {
  597. od_set_statusline(btCurrentStatusLine);
  598. od_control.od_update_status_now = FALSE;
  599. }
  600. /* Update status line when needed. */
  601. if(nNextStatusUpdateTime < time(NULL) || bForceStatusUpdate)
  602. {
  603. statup:
  604. nNextStatusUpdateTime = time(NULL) + STATUS_UPDATE_PERIOD;
  605. /* Turn off status line update force flag */
  606. bForceStatusUpdate = FALSE;
  607. if(od_control.od_status_on && btCurrentStatusLine != 8)
  608. {
  609. /* Store console settings. */
  610. ODStoreTextInfo();
  611. /* Enable writes to whole screen. */
  612. ODScrnSetBoundary(1, 1, 80, 25);
  613. ODScrnEnableCaret(FALSE);
  614. (*pfCurrentPersonality)((BYTE)(10 + btCurrentStatusLine));
  615. ODRestoreTextInfo();
  616. ODScrnEnableCaret(TRUE);
  617. }
  618. }
  619. #endif
  620. ODKrnlTimeUpdate();
  621. ODTimerStart(&RunKernelTimer, 250);
  622. OD_API_EXIT();
  623. bKernelActive = FALSE;
  624. #endif /* !OD_MULTITHREADED */
  625. }
  626. /* ----------------------------------------------------------------------------
  627. * ODKrnlHandleLocalKey()
  628. *
  629. * Called when a key is pressed on the local keyboard that should be placed
  630. * in the common local/remote input queue. This function is not called for
  631. * sysop function keys.
  632. *
  633. * Parameters: wKeyCode
  634. *
  635. * Return: void
  636. */
  637. void ODKrnlHandleLocalKey(WORD wKeyCode)
  638. {
  639. /* If local keyboard input by sysop has not been disabled. */
  640. if(!(od_control.od_disable & DIS_LOCAL_INPUT))
  641. {
  642. if((wKeyCode & 0xff) == 0)
  643. {
  644. ODKrnlHandleReceivedChar('\0', FALSE);
  645. ODKrnlHandleReceivedChar((char)(wKeyCode >> 8), FALSE);
  646. }
  647. else
  648. {
  649. ODKrnlHandleReceivedChar((char)wKeyCode, FALSE);
  650. }
  651. }
  652. }
  653. /* ----------------------------------------------------------------------------
  654. * ODKrnlHandleReceivedChar() *** PRIVATE FUNCTION ***
  655. *
  656. * Called when a character is received from the local or remote system.
  657. *
  658. * Parameters: chReceived - Character that should be handled.
  659. *
  660. * bFromRemote - TRUE if this character was received from the
  661. * remote system, FALSE if it originated from the
  662. * local console.
  663. *
  664. * Return: void
  665. */
  666. static void ODKrnlHandleReceivedChar(char chReceived, BOOL bFromRemote)
  667. {
  668. tODInputEvent InputEvent;
  669. /* If we are operating in remote mode, and remote user keyboard has been */
  670. /* disabled by the sysop, then return, ignoring this character. */
  671. if(bFromRemote && !od_control.od_user_keyboard_on)
  672. {
  673. return;
  674. }
  675. /* Add this input event to the local/remote common input queue. */
  676. InputEvent.EventType = EVENT_CHARACTER;
  677. InputEvent.bFromRemote = bFromRemote;
  678. InputEvent.chKeyPress = chReceived;
  679. ODInQueueAddEvent(hODInputQueue, &InputEvent);
  680. /* Update last control key information. */
  681. switch(chReceived)
  682. {
  683. case 's':
  684. case 'S':
  685. case 3:
  686. case 11:
  687. case 0x18:
  688. chLastControlKey = 's';
  689. break;
  690. case 'p':
  691. case 'P':
  692. chLastControlKey = 'p';
  693. }
  694. }
  695. /* ----------------------------------------------------------------------------
  696. * ODKrnlTimeUpdate() *** PRIVATE FUNCTION ***
  697. *
  698. * Performs regular updating of time remaining online, inactivity time, and
  699. * forces OpenDoors to exit if a time limit has been exceeded.
  700. *
  701. * Parameters: None
  702. *
  703. * Return: void
  704. */
  705. static void ODKrnlTimeUpdate(void)
  706. {
  707. time_t CurrentTime;
  708. static char szTemp[80];
  709. /* Obtain the current time. */
  710. CurrentTime = time(NULL);
  711. /* If inactivity setting has changed. */
  712. if(nLastInactivitySetting != od_control.od_inactivity)
  713. {
  714. /* If it was previously disabled. */
  715. if(nLastInactivitySetting == 0)
  716. {
  717. /* Prevent immediate timeout. */
  718. ODInQueueResetLastActivity(hODInputQueue);
  719. }
  720. /* Store current value. */
  721. nLastInactivitySetting = od_control.od_inactivity;
  722. }
  723. /* Check user keyboard inactivity. */
  724. if((ODInQueueGetLastActivity(hODInputQueue) + od_control.od_inactivity)
  725. < CurrentTime)
  726. {
  727. /* If timeout, display message. */
  728. if(od_control.od_inactivity != 0 && !od_control.od_disable_inactivity)
  729. {
  730. if(od_control.od_time_msg_func == NULL)
  731. {
  732. od_disp_str(od_control.od_inactivity_timeout);
  733. }
  734. else
  735. {
  736. (*od_control.od_time_msg_func)(od_control.od_inactivity_timeout);
  737. }
  738. /* End connection. */
  739. ODKrnlForceOpenDoorsShutdown(ERRORLEVEL_INACTIVITY);
  740. }
  741. }
  742. /* If less than 5s left of inactivity. */
  743. else if(ODInQueueGetLastActivity(hODInputQueue) + od_control.od_inactivity
  744. < CurrentTime + od_control.od_inactive_warning)
  745. {
  746. if(!bWarnedAboutInactivity && od_control.od_inactivity != 0
  747. && !od_control.od_disable_inactivity)
  748. {
  749. /* Warn the user. */
  750. if(od_control.od_time_msg_func == NULL)
  751. {
  752. od_disp_str(od_control.od_inactivity_warning);
  753. }
  754. else
  755. {
  756. (*od_control.od_time_msg_func)(od_control.od_inactivity_warning);
  757. }
  758. /* Don't warn the user a second time. */
  759. bWarnedAboutInactivity = TRUE;
  760. }
  761. }
  762. else
  763. {
  764. /* Re-enable inactivity warning. */
  765. bWarnedAboutInactivity = FALSE;
  766. }
  767. /* If chat mode is active. */
  768. if(od_control.od_chat_active)
  769. {
  770. /* Prevent the user's time from being drained. */
  771. nNextTimeDeductTime = time(NULL) + 60;
  772. }
  773. /* If 1 minute has passed since last time update. */
  774. if(CurrentTime >= nNextTimeDeductTime)
  775. {
  776. /* Next time update should occur 60 seconds after this one was */
  777. /* scheduled. */
  778. nNextTimeDeductTime += 60;
  779. /* Force status line to be updated immediately. */
  780. bForceStatusUpdate = TRUE;
  781. /* Decrement time left. */
  782. --od_control.user_timelimit;
  783. /* If the user's time limit is close to expiring, then notify */
  784. /* the user. */
  785. if(od_control.user_timelimit <= 3 &&
  786. od_control.user_timelimit > 0 &&
  787. !(od_control.od_disable & DIS_TIMEOUT))
  788. {
  789. /* If less than 3 mins left, tell user. */
  790. sprintf(szTemp, od_control.od_time_warning,
  791. od_control.user_timelimit);
  792. if(od_control.od_time_msg_func == NULL)
  793. {
  794. od_disp_str(szTemp);
  795. }
  796. else
  797. {
  798. (*od_control.od_time_msg_func)(szTemp);
  799. }
  800. }
  801. #ifdef ODPLAT_WIN32
  802. ODFrameUpdateTimeDisplay();
  803. #endif /* ODPLAT_WIN32 */
  804. }
  805. /* If user has no time left. */
  806. if(od_control.user_timelimit <= 0
  807. && !(od_control.od_disable & DIS_TIMEOUT))
  808. {
  809. /* Notify the user. */
  810. if(od_control.od_time_msg_func == NULL)
  811. {
  812. od_disp_str(od_control.od_no_time);
  813. }
  814. else
  815. {
  816. (*od_control.od_time_msg_func)(od_control.od_no_time);
  817. }
  818. /* Force OpenDoors to shutdown. */
  819. ODKrnlForceOpenDoorsShutdown(ERRORLEVEL_TIMEOUT);
  820. }
  821. }
  822. /* ----------------------------------------------------------------------------
  823. * ODKrnlForceOpenDoorsShutdown()
  824. *
  825. * Called to force the application to exit due to some event in OpenDoors,
  826. * such as loss of carrier, user inactivity timeout, the hangup command
  827. * being chosen by the system operator, etc. The only time when OpenDoors
  828. * is shutdown without going through this function should be as a result of
  829. * an explicit call to od_exit() by the client application.
  830. *
  831. * Parameters: btReasonForShutdown - An OpenDoors exit reason code.
  832. *
  833. * Return: Never returns.
  834. */
  835. void ODKrnlForceOpenDoorsShutdown(BYTE btReasonForShutdown)
  836. {
  837. BOOL bHangup;
  838. #ifdef OD_MULTITHREADED
  839. /* First, wait until an OpenDoors API is active. This way, we won't */
  840. /* interrupt any client application operations that may leave the */
  841. /* system in an unstable state (for instance, interrupting some file */
  842. /* I/O operations). */
  843. ODKrnlWaitForExclusiveControl();
  844. #endif /* OD_MULTITHREADED */
  845. bKernelActive = TRUE;
  846. /* Determine whether we should hangup on the user before exiting. */
  847. if(btReasonForShutdown == ERRORLEVEL_HANGUP
  848. || btReasonForShutdown == ERRORLEVEL_INACTIVITY)
  849. {
  850. bHangup = TRUE;
  851. }
  852. else
  853. {
  854. bHangup = FALSE;
  855. }
  856. /* Record exit reason in global variable. */
  857. btExitReason = btReasonForShutdown - 1;
  858. /* Use the client-defined errorlevel, if any. */
  859. if(od_control.od_errorlevel[0])
  860. {
  861. od_exit(od_control.od_errorlevel[btReasonForShutdown], bHangup);
  862. }
  863. /* Otherwise, use the default OpenDoors errorlevel. */
  864. else
  865. {
  866. od_exit(btReasonForShutdown - 1, bHangup);
  867. }
  868. }
  869. /* ========================================================================= */
  870. /* OpenDoors Kernel multithreaded implementation. */
  871. /* ========================================================================= */
  872. #ifdef OD_MULTITHREADED
  873. /* ----------------------------------------------------------------------------
  874. * ODKrnlRemoteInputThread() *** PRIVATE FUNCTION ***
  875. *
  876. * Code for the remote input thread. This thread executes an infinite loop,
  877. * blocking until a character is received from the remote system, and then
  878. * adding this character to the common local/remote input queue. This thread
  879. * should be given higher than normal priority.
  880. *
  881. * In non-multithreaded versions of OpenDoors, the task of checking for new
  882. * characters from the remote system and adding them to the common input
  883. * queue is performed on each call to od_kernel().
  884. *
  885. * Parameters: As dictated for any thread function.
  886. *
  887. * Return: As dictated for any thread function.
  888. */
  889. DWORD OD_THREAD_FUNC ODKrnlRemoteInputThread(void *pParam)
  890. {
  891. char chReceived;
  892. /* We keep looping until someone else terminates this thread. */
  893. for(;;)
  894. {
  895. /* Get next character from the modem, blocking if no character */
  896. /* is waiting. */
  897. ODComGetByte(hSerialPort, &chReceived, TRUE);
  898. /* Handle this received character, adding it to the local/remote */
  899. /* common input queue, if appropriate. */
  900. ODKrnlHandleReceivedChar(chReceived, TRUE);
  901. }
  902. return(0);
  903. }
  904. /* ----------------------------------------------------------------------------
  905. * ODKrnlNoCarrierThread() *** PRIVATE FUNCTION ***
  906. *
  907. * Thread which performs carrier detection. Normally, this thread doesn't
  908. * execute at all, but instead blocks waiting for a no carrier serial port
  909. * event. Only when the carrier detect signal goes low does this thread
  910. * execute, performing its one purpose in live - to trigger an OpenDoors
  911. * shutdown. This thread should be given higher than normal priority.
  912. *
  913. * This thread should only be created when OpenDoors is operating in remote
  914. * mode.
  915. *
  916. * In non-multithreaded versions of OpenDoors, this task is performed by
  917. * od_kernel().
  918. *
  919. * Parameters: As dictated for any thread function.
  920. *
  921. * Return: As dictated for any thread function.
  922. */
  923. DWORD OD_THREAD_FUNC ODKrnlNoCarrierThread(void *pParam)
  924. {
  925. /* Block until the carrier detect signal goes low with carrier */
  926. /* detection enabled. */
  927. for(;;)
  928. {
  929. /* Wait for carrier detect signal to go low. */
  930. ODComWaitEvent(hSerialPort, kNoCarrier);
  931. /* If carrier detection has not been disabled, then we have found */
  932. /* a condition where OpenDoors should exit. */
  933. if(!(od_control.od_disable&DIS_CARRIERDETECT)) break;
  934. /* If we have no carrier but carrier detection is currently */
  935. /* disabled, then we sleep for a while before checking again. */
  936. /* This isn't a very elegant implementation, and perhaps a */
  937. /* better approach will be used for future versions. */
  938. od_sleep(NO_CARRIER_THREAD_SLEEP_TIME);
  939. }
  940. /* Force OpenDoors to exit. */
  941. ODKrnlForceOpenDoorsShutdown(ERRORLEVEL_NOCARRIER);
  942. return(0);
  943. }
  944. /* ----------------------------------------------------------------------------
  945. * ODKrnlTimeUpdateThread() *** PRIVATE FUNCTION ***
  946. *
  947. * Thread which performs time limit updating and checking. This thread executes
  948. * an infinite loop, sleeping for several seconds, waking up to perform time
  949. * limit updating, and then going back to sleep. This thead should typically
  950. * operate at normal priority.
  951. *
  952. * In non-multithreaded versions of OpenDoors, this task is performed by
  953. * od_kernel().
  954. *
  955. * Parameters: As dictated for any thread function.
  956. *
  957. * Return: As dictated for any thread function.
  958. */
  959. DWORD OD_THREAD_FUNC ODKrnlTimeUpdateThread(void *pParam)
  960. {
  961. /* We keep looping until someone else terminates this thread. */
  962. for(;;)
  963. {
  964. /* Sleep until it is time to do the next update. */
  965. od_sleep(TIME_UPDATE_THREAD_SLEEP_TIME);
  966. /* Now, perform time update. */
  967. ODKrnlTimeUpdate();
  968. }
  969. return(0);
  970. }
  971. /* ----------------------------------------------------------------------------
  972. * ODKrnlWaitForExclusiveControl() *** PRIVATE FUNCTION ***
  973. *
  974. * Claims exclusive control of the application by the OpenDoors kernel. This is
  975. * required to ensure that the client application is not busy when the
  976. * OpenDoors kernel interrupts other operations for one reason or another
  977. * (for example, to start chat mode or to force the program to exit).
  978. *
  979. * Parameters: None
  980. *
  981. * Return: void
  982. */
  983. static void ODKrnlWaitForExclusiveControl(void)
  984. {
  985. /* If we already have exclusive control, then don't do anything. */
  986. if(bHaveExclusiveControl) return;
  987. /* Wait until an OpenDoors API is active. */
  988. ODSemaphoreDown(hODActiveSemaphore, OD_NO_TIMEOUT);
  989. /* Now, suspend the client thread. */
  990. ASSERT(hClientThread != NULL);
  991. ODThreadSuspend(hClientThread);
  992. /* Record that we now have exclusive control. */
  993. bHaveExclusiveControl = TRUE;
  994. }
  995. /* ----------------------------------------------------------------------------
  996. * ODKrnlGiveUpExclusiveControl() *** PRIVATE FUNCTION ***
  997. *
  998. * Relinguishes exclusive control of the application by the OpenDoors kernel.
  999. * A call to this function should only take place after a previous call to
  1000. * ODKrnlWaitForExclusiveControl().
  1001. *
  1002. * Parameters: None
  1003. *
  1004. * Return: void
  1005. */
  1006. static void ODKrnlGiveUpExclusiveControl(void)
  1007. {
  1008. /* If we don't have exclusive control, then this call doesn't do */
  1009. /* anything. */
  1010. if(!bHaveExclusiveControl) return;
  1011. /* First, restart the client thread. */
  1012. ASSERT(hClientThread != NULL);
  1013. ODThreadResume(hClientThread);
  1014. /* Now, allow currently active OpenDoors API to return control */
  1015. /* to the client application. */
  1016. ODSemaphoreUp(hODActiveSemaphore, 1);
  1017. /* Note that we no longer have exclusive control. */
  1018. bHaveExclusiveControl = FALSE;
  1019. }
  1020. #endif /* OD_MULTITHREADED */
  1021. /* ========================================================================= */
  1022. /* OpenDoors chat mode. */
  1023. /* ========================================================================= */
  1024. BOOL bChatted;
  1025. BOOL bSysopColor;
  1026. #ifdef OD_MULTITHREADED
  1027. /* ----------------------------------------------------------------------------
  1028. * ODKrnlChatThread() *** PRIVATE FUNCTION ***
  1029. *
  1030. * Thread which implements sysop <-> remote user chat mode.
  1031. *
  1032. * Parameters: As dictated for any thread function.
  1033. *
  1034. * Return: As dictated for any thread function.
  1035. */
  1036. DWORD OD_THREAD_FUNC ODKrnlChatThread(void *pParam)
  1037. {
  1038. BOOL bTriggeredInsideOpenDoors = bChatActivatedInternally;
  1039. /* The chat thread doesn't start up chat mode until the kernel has */
  1040. /* exclusive control of the client application. */
  1041. if(bTriggeredInsideOpenDoors)
  1042. {
  1043. ODKrnlWaitForExclusiveControl();
  1044. }
  1045. /* Now, execute the chat mode loop. */
  1046. ODKrnlChatMode();
  1047. /* If we get here, then we are responsible for relinguishing exclusive */
  1048. /* control of the application. */
  1049. if(bTriggeredInsideOpenDoors)
  1050. {
  1051. ODKrnlGiveUpExclusiveControl();
  1052. }
  1053. /* Exit the chat thread. */
  1054. return(0);
  1055. }
  1056. /* ----------------------------------------------------------------------------
  1057. * ODKrnlStartChatThread()
  1058. *
  1059. * Starts the chat mode thread.
  1060. *
  1061. * Parameters: bTriggeredInternally - TRUE if chat mode has been triggered
  1062. * inside OpenDoors, or FALSE if it has
  1063. * been triggered by a call to od_chat().
  1064. *
  1065. * Return: kODRCSuccess on success, or an error code on failure.
  1066. */
  1067. tODResult ODKrnlStartChatThread(BOOL bTriggeredInternally)
  1068. {
  1069. tODResult Result;
  1070. bChatActivatedInternally = bTriggeredInternally;
  1071. Result = ODThreadCreate(&hChatThread, ODKrnlChatThread, NULL);
  1072. if(Result != kODRCSuccess)
  1073. {
  1074. return(Result);
  1075. }
  1076. /* If chat mode command has been chosen, then toggle chat */
  1077. /* mode on or off. */
  1078. od_control.od_chat_active = TRUE;
  1079. #ifdef ODPLAT_WIN32
  1080. /* Update the enabled and checked state of commands. */
  1081. ODFrameUpdateCmdUI();
  1082. #endif /* ODPLAT_WIN32 */
  1083. return(kODRCSuccess);
  1084. }
  1085. #endif /* OD_MULTITHREADED */
  1086. /* ----------------------------------------------------------------------------
  1087. * ODKrnlEndChatMode()
  1088. *
  1089. * Forces chat mode to exit.
  1090. *
  1091. * Parameters: None
  1092. *
  1093. * Return: void
  1094. */
  1095. void ODKrnlEndChatMode(void)
  1096. {
  1097. #ifdef OD_MULTITHREADED
  1098. /* Shutdown the chat thread. */
  1099. ODThreadTerminate(hChatThread);
  1100. /* Perform post-chat cleanup operations. */
  1101. ODKrnlChatCleanup();
  1102. #else /* !OD_MULTITHREADED */
  1103. /* Turn off chat mode. */
  1104. od_control.od_chat_active = FALSE;
  1105. #endif /* !OD_MULTITHREADED */
  1106. }
  1107. /* ----------------------------------------------------------------------------
  1108. * od_chat()
  1109. *
  1110. * Allows the client application to activate the line-by-line default chat
  1111. * mode provided by OpenDoors, allowing the local sysop and remote user to
  1112. * communicate with one another in real time.
  1113. *
  1114. * Parameters: none
  1115. *
  1116. * Return: void
  1117. */
  1118. ODAPIDEF void ODCALL od_chat(void)
  1119. {
  1120. /* Log function entry if running in trace mode. */
  1121. TRACE(TRACE_API, "od_chat()");
  1122. /* Set the main chat active flag in od_control. */
  1123. od_control.od_chat_active = TRUE;
  1124. /* Initialize OpenDoors if it hasn't already been done. */
  1125. if(!bODInitialized) od_init();
  1126. OD_API_ENTRY();
  1127. #ifdef OD_MULTITHREADED
  1128. /* In multithreaded versions of OpenDoors, od_chat() causes the chat */
  1129. /* mode thread to be started, which in turn implements chat mode. */
  1130. /* od_chat() only returns when this thread exits. */
  1131. if(ODKrnlStartChatThread(FALSE) != kODRCSuccess)
  1132. {
  1133. od_control.od_error = ERR_GENERALFAILURE;
  1134. OD_API_EXIT();
  1135. }
  1136. /* Now, wait for the chat thread to exit. */
  1137. ODThreadWaitForExit(hChatThread);
  1138. /* Now, note that the chat thread no longer exists. */
  1139. hChatThread = NULL;
  1140. #else /* !OD_MULTITHREADED */
  1141. /* In non-multithreaded versions, a call to od_chat() maps directly to a */
  1142. /* call to ODKrnlChatMode(), which implements chat mode. */
  1143. ODKrnlChatMode();
  1144. #endif /* !OD_MULTITHREADED */
  1145. OD_API_EXIT();
  1146. }
  1147. /* ----------------------------------------------------------------------------
  1148. * ODKrnlChatMode() *** PRIVATE FUNCTION ***
  1149. *
  1150. * Implements the OpenDoors chat mode.
  1151. *
  1152. * Parameters: None
  1153. *
  1154. * Return: void
  1155. */
  1156. static void ODKrnlChatMode(void)
  1157. {
  1158. BYTE chKeyPressed;
  1159. char szCurrentWord[79];
  1160. BYTE btWordLength = 0;
  1161. BYTE btCurrentColumn = 0;
  1162. char *pchCurrent;
  1163. BYTE btCount;
  1164. #ifndef OD_MULTITHREADED
  1165. tODTimer Timer;
  1166. #endif /* !OD_MULTITHREADED */
  1167. /* Empty current word string. */
  1168. szCurrentWord[0] = '\0';
  1169. /* Save current display color attribute. */
  1170. nChatOriginalAttrib = od_control.od_cur_attrib;
  1171. /* Record that sysop has entered chat mode. */
  1172. bChatted = TRUE;
  1173. /* Turn off "user wants to chat" indicator, and force the status line. */
  1174. /* to be updated. */
  1175. od_control.user_wantchat = FALSE;
  1176. #ifdef ODPLAT_WIN32
  1177. ODFrameUpdateWantChat();
  1178. #endif /* ODPLAT_WIN32 */
  1179. bForceStatusUpdate = TRUE;
  1180. CALL_KERNEL_IF_NEEDED();
  1181. /* Note that chat mode is now active. */
  1182. od_control.od_chat_active = TRUE;
  1183. /* If a pre-chat function hook has been defined, then call it. */
  1184. if(od_control.od_cbefore_chat!=NULL)
  1185. {
  1186. bShellChatActive = TRUE;
  1187. (*od_control.od_cbefore_chat)();
  1188. bShellChatActive = FALSE;
  1189. /* If chat has been deactivated, then return right away */
  1190. if(!od_control.od_chat_active) goto cleanup;
  1191. }
  1192. /* Display a message indicating that the sysop has entered chat mode. */
  1193. od_set_attrib(od_control.od_chat_color1);
  1194. if(od_control.od_before_chat != NULL)
  1195. od_disp_str(od_control.od_before_chat);
  1196. /* Currently set to sysop color. */
  1197. bSysopColor = TRUE;
  1198. /* If the logfile system is hooked up, then write a log entry */
  1199. /* indicating that the sysop has entered chat mode. */
  1200. if(pfLogWrite != NULL)
  1201. {
  1202. (*pfLogWrite)(9);
  1203. }
  1204. #ifndef OD_MULTITHREADED
  1205. /* Start a timer that will elapse after 25 milliseconds. */
  1206. ODTimerStart(&Timer, CHAT_YIELD_PERIOD);
  1207. #endif /* !OD_MULTITHREADED */
  1208. /* Loop while sysop chat mode is stilil on. */
  1209. while(od_control.od_chat_active)
  1210. {
  1211. /* Obtain the next key from the user. */
  1212. #ifdef OD_MULTITHREADED
  1213. chKeyPressed = od_get_key(TRUE);
  1214. #else /* !OD_MULTITHREADED */
  1215. chKeyPressed = od_get_key(FALSE);
  1216. #endif /* !OD_MULTITHREADED */
  1217. /* If color not set correctly. */
  1218. if((od_control.od_last_input && !bSysopColor)
  1219. || (!od_control.od_last_input && bSysopColor))
  1220. {
  1221. /* If sysop was last person to type. */
  1222. if(od_control.od_last_input)
  1223. {
  1224. /* Switch to sysop text color. */
  1225. od_set_attrib(od_control.od_chat_color1);
  1226. }
  1227. else
  1228. {
  1229. /* Otherwise, switch to the user text color. */
  1230. od_set_attrib(od_control.od_chat_color2);
  1231. }
  1232. /* Record current color setting. */
  1233. bSysopColor = od_control.od_last_input;
  1234. }
  1235. /* If this is a displayable character. */
  1236. if(chKeyPressed >= 32)
  1237. {
  1238. /* Display the character that was typed. */
  1239. od_putch(chKeyPressed);
  1240. /* If the user pressed spacebar, then this is the end of the */
  1241. /* previous word. */
  1242. if(chKeyPressed == 32)
  1243. {
  1244. btWordLength = 0;
  1245. szCurrentWord[0] = 0;
  1246. }
  1247. /* Add this character to the current word, if we haven't exceeded */
  1248. /* the maximum word length. */
  1249. else if(btWordLength < 70)
  1250. {
  1251. szCurrentWord[btWordLength++] = chKeyPressed;
  1252. szCurrentWord[btWordLength] = '\0';
  1253. }
  1254. /* If we are not yet at the end of the line, then increment the */
  1255. /* current column number. */
  1256. if(btCurrentColumn < 75)
  1257. {
  1258. ++btCurrentColumn;
  1259. }
  1260. /* If we are at the end of the line. */
  1261. else
  1262. {
  1263. /* If the current word should be wrapped to the next line. */
  1264. if(btWordLength < 70 && btWordLength > 0)
  1265. {
  1266. /* Generate a string to erase the word from the current line. */
  1267. pchCurrent = (char *)szODWorkString;
  1268. for(btCount = 0; btCount < btWordLength; ++btCount)
  1269. {
  1270. *(pchCurrent++) = 8;
  1271. }
  1272. for(btCount = 0; btCount < btWordLength; ++btCount)
  1273. {
  1274. *(pchCurrent++) = ' ';
  1275. }
  1276. *pchCurrent = '\0';
  1277. /* Display the string to erase the old word. */
  1278. od_disp_str(szODWorkString);
  1279. /* Move to the next line. */
  1280. od_disp_str("\n\r");
  1281. /* Redisplay the word on the next line. */
  1282. od_disp_str(szCurrentWord);
  1283. /* Update current column number. */
  1284. btCurrentColumn = btWordLength;
  1285. }
  1286. /* If we have reached the end of the line, but word wrap should */
  1287. /* not be performed. */
  1288. else
  1289. {
  1290. /* Move to the next line. */
  1291. od_disp_str("\n\r");
  1292. /* Update the current column number. */
  1293. btCurrentColumn = 0;
  1294. }
  1295. /* Reset the current word information. */
  1296. btWordLength = 0;
  1297. szCurrentWord[0] = 0;
  1298. }
  1299. }
  1300. /* If the backspace key was pressed. */
  1301. else if(chKeyPressed == 8)
  1302. {
  1303. /* Send backspace sequence. */
  1304. od_disp_str(szBackspaceWithDelete);
  1305. /* If we are in the middle of a word, then we must remove the */
  1306. /* last character of the word. */
  1307. if(btWordLength > 0)
  1308. {
  1309. szCurrentWord[--btWordLength] = '\0';
  1310. }
  1311. /* Update the current column number. */
  1312. if(btCurrentColumn > 0) --btCurrentColumn;
  1313. }
  1314. /* If the enter key was pressed. */
  1315. else if(chKeyPressed == 13)
  1316. {
  1317. /* Send carriage return / line feed sequence. */
  1318. od_disp_str("\n\r");
  1319. /* Reset the current word contents. */
  1320. btWordLength = 0;
  1321. szCurrentWord[0] = 0;
  1322. /* Update the current column number. */
  1323. btCurrentColumn = 0;
  1324. }
  1325. /* If the sysop pressed the escape key. */
  1326. else if(chKeyPressed == 27 && od_control.od_last_input)
  1327. {
  1328. /* Exit chat mode. */
  1329. goto cleanup;
  1330. }
  1331. #ifndef OD_MULTITHREADED
  1332. /* Give up processor after 25 milliseconds elapsed. */
  1333. else if(ODTimerElapsed(&Timer))
  1334. {
  1335. od_sleep(0);
  1336. /* Restart the timer, so that it will elapse after another */
  1337. /* 25 milliseconds. */
  1338. ODTimerStart(&Timer, CHAT_YIELD_PERIOD);
  1339. }
  1340. #endif /* !OD_MULTITHREADED */
  1341. }
  1342. cleanup:
  1343. ODKrnlChatCleanup();
  1344. }
  1345. /* ----------------------------------------------------------------------------
  1346. * ODKrnlChatCleanup() *** PRIVATE FUNCTION ***
  1347. *
  1348. * Performs post-chat operations, such as resetting the original display
  1349. * color, etc.
  1350. *
  1351. * Parameters: None
  1352. *
  1353. * Return: void
  1354. */
  1355. static void ODKrnlChatCleanup(void)
  1356. {
  1357. od_set_attrib(od_control.od_chat_color1);
  1358. /* Indicate that chat mode is exiting. */
  1359. if(od_control.od_after_chat != NULL)
  1360. {
  1361. od_disp_str(od_control.od_after_chat);
  1362. }
  1363. /* If an after chat function has been provided, then call it. */
  1364. if(od_control.od_cafter_chat != NULL)
  1365. {
  1366. bShellChatActive = TRUE;
  1367. (*od_control.od_cafter_chat)();
  1368. bShellChatActive = FALSE;
  1369. }
  1370. /* If the logfile system is hooked up, then write a line to the log */
  1371. /* indicating that chat mode has been exited. */
  1372. if(pfLogWrite != NULL)
  1373. {
  1374. (*pfLogWrite)(10);
  1375. }
  1376. /* Restore original display color attribute. */
  1377. od_set_attrib(nChatOriginalAttrib);
  1378. /* Record that chat mode is no longer active. */
  1379. od_control.od_chat_active = FALSE;
  1380. #ifdef ODPLAT_WIN32
  1381. /* Update the enabled and checked state of commands. */
  1382. ODFrameUpdateCmdUI();
  1383. #endif /* ODPLAT_WIN32 */
  1384. #ifdef OD_MULTITHREADED
  1385. if(bChatActivatedInternally)
  1386. {
  1387. ODKrnlGiveUpExclusiveControl();
  1388. }
  1389. #endif
  1390. }
  1391. #ifdef ODPLAT_NIX
  1392. #ifdef USE_KERNEL_SIGNAL
  1393. /* ----------------------------------------------------------------------------
  1394. * sig_run_kernel(sig) *** PRIVATE FUNCTION ***
  1395. *
  1396. * Runs od_kernel() on a SIGALRM
  1397. *
  1398. */
  1399. static void sig_run_kernel(int sig)
  1400. {
  1401. od_kernel();
  1402. }
  1403. /* ----------------------------------------------------------------------------
  1404. * sig_run_kernel(sig) *** PRIVATE FUNCTION ***
  1405. *
  1406. * Runs od_kernel() on a SIGALRM
  1407. *
  1408. */
  1409. static void sig_get_char(int sig)
  1410. {
  1411. static char ch;
  1412. /* Loop, obtaining any new characters from the serial port and */
  1413. /* adding them to the common local/remote input queue. */
  1414. while(ODComGetByte(hSerialPort, &ch, FALSE) == kODRCSuccess)
  1415. {
  1416. ODKrnlHandleReceivedChar(ch, TRUE);
  1417. }
  1418. }
  1419. static void sig_no_carrier(int sig)
  1420. {
  1421. if(od_control.baud != 0 && )
  1422. {
  1423. if(!(od_control.od_disable&DIS_CARRIERDETECT))
  1424. ODKrnlForceOpenDoorsShutdown(ERRORLEVEL_NOCARRIER);
  1425. }
  1426. }
  1427. #endif
  1428. #endif