home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume41 / ison / part01 next >
Text File  |  1993-12-13  |  59KB  |  2,225 lines

  1. Newsgroups: comp.sources.misc
  2. From: mgleason@cse.unl.edu (Mike Gleason)
  3. Subject: v41i031:  ison - Network user logon monitor, Part01/01
  4. Message-ID: <1993Dec14.042025.3559@sparky.sterling.com>
  5. X-Md4-Signature: 0b1c189ea02d39d78e8740738e61edb2
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: NCEMRSoft
  8. Date: Tue, 14 Dec 1993 04:20:25 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: mgleason@cse.unl.edu (Mike Gleason)
  12. Posting-number: Volume 41, Issue 31
  13. Archive-name: ison/part01
  14. Environment: UNIX, finger, optionally rpcsvc
  15. Supersedes: ison: Volume 34, Issue 4
  16.  
  17. Version 5.0 changes:
  18.    + Complete rewrite; numerous minor changes.
  19.    + RPC support added, drastically improving efficiency.
  20.    + Can detect idle time (even for Finger), and can poll until not-idle.
  21.    + Multiple hosts, Multiple users!
  22.    + Monitor mode, where ison prints logon and logoff messages
  23.    + Commands can contain %flags that are expanded to the appropriate value
  24.      before the command is run, i.e. "write %u %t" executes
  25.      "write <username> <tty>"
  26.    + Powerful user files can be used, to use one ison process to do
  27.      different things with different users.
  28.    + If the information is available, ison can tell you the actual
  29.      logon time and tty of remote (and local) users.
  30.    + More detailed output printed.
  31.    + Manual page rewritten.
  32.  
  33. IsOn's primary purpose is to let you know when someone logs on. You could
  34. always sit there at your terminal typing 'finger' or 'who' every 5 minutes,
  35. but that's boring and unproductive. IsOn makes this easy. If you wanted to
  36. know the instant I logged on, all it would take is a simple:
  37.  
  38.    ison mgleason@cse.unl.edu
  39.  
  40. When I do log on, ison would respond:
  41.  
  42.    ** IsOn: mgleason@cse.unl.edu logged on to ttyq28 since 8:41 PM
  43.             and is not idle.
  44.  
  45. IsOn lowers it's priority automatically, so it takes very little CPU,
  46. and spares you the trouble of remembering to use 'nice.' It also puts
  47. itself in the background automatically. For remote addresses (those in
  48. dude@machine.domain format) the rusers function from the rpcsvc library
  49. is used, or the 'finger' utility if it has to, and for a user on the same
  50. machine that you are on, IsOn will simply walk the 'utmp' file.
  51.  
  52. Enjoy!
  53. --mike gleason = mgleason@cse.unl.edu
  54.  
  55. #! /bin/sh
  56. # This is a shell archive.  Remove anything before this line, then unpack
  57. # it by saving it into a file and typing "sh file".  To overwrite existing
  58. # files, type "sh file -c".  You can also feed this as standard input via
  59. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  60. # will see the following message at the end:
  61. #        "End of shell archive."
  62. # Contents:  INSTALL patchlevel.h ison.1 config.h ison.h Makefile
  63. #   ison.c
  64. # Wrapped by mgleason@cse on Thu Dec  9 23:37:31 1993
  65. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  66. if test -f 'INSTALL' -a "${1}" != "-c" ; then 
  67.   echo shar: Will not clobber existing file \"'INSTALL'\"
  68. else
  69. echo shar: Extracting \"'INSTALL'\" \(701 characters\)
  70. sed "s/^X//" >'INSTALL' <<'END_OF_FILE'
  71. XIf you're lazy, just type "make" and see if compiles.  Otherwise read
  72. Xand edit config.h, then the Makefile.
  73. X
  74. XThe two biggest problems I could imagine would be (1) the variable-arguments
  75. Xpart (you MUST have one of <stdarg.h> or <varargs.h>), and (2) Getting the
  76. Xcode to link with your RPC libraries.  config.h lets you choose for (1),
  77. Xbut for (2) you MUST have the rpcsvc library, and you MAY need an additional
  78. Xlibrary or two that the rpcsvc library requires to link.  For my system I
  79. Xneed both the "rpcsvc" library and the "sun" library.  The program can
  80. Xlive without RPC, but it is MUCH more efficient net-friendly, so I
  81. Xhighly recommend it.  config.h will let you try compiling without it,
  82. Xthough.
  83. END_OF_FILE
  84. if test 701 -ne `wc -c <'INSTALL'`; then
  85.     echo shar: \"'INSTALL'\" unpacked with wrong size!
  86. fi
  87. # end of 'INSTALL'
  88. fi
  89. if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  90.   echo shar: Will not clobber existing file \"'patchlevel.h'\"
  91. else
  92. echo shar: Extracting \"'patchlevel.h'\" \(795 characters\)
  93. sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
  94. X/*
  95. X
  96. Xv5.0.0 -- December 7, 1993.  Initial release of version 5.
  97. X   + Complete rewrite; numerous minor changes.
  98. X   + RPC support added, drastically improving efficiency.
  99. X   + Can detect idle time (even for Finger), and can poll until not-idle.
  100. X   + Multiple hosts, Multiple users!
  101. X   + Monitor mode, where ison prints logon and logoff messages
  102. X   + Commands can contain %flags that are expanded to the appropriate value
  103. X     before the command is run, i.e. "write %u %t" executes
  104. X     "write <username> <tty>"
  105. X   + Powerful user files can be used, to use one ison process to do
  106. X     different things with different users.
  107. X   + If the information is available, ison can tell you the actual
  108. X     logon time and tty of remote (and local) users.
  109. X   + More detailed output printed.
  110. X   + Manual page rewritten.
  111. X
  112. X*/
  113. END_OF_FILE
  114. if test 795 -ne `wc -c <'patchlevel.h'`; then
  115.     echo shar: \"'patchlevel.h'\" unpacked with wrong size!
  116. fi
  117. # end of 'patchlevel.h'
  118. fi
  119. if test -f 'ison.1' -a "${1}" != "-c" ; then 
  120.   echo shar: Will not clobber existing file \"'ison.1'\"
  121. else
  122. echo shar: Extracting \"'ison.1'\" \(7904 characters\)
  123. sed "s/^X//" >'ison.1' <<'END_OF_FILE'
  124. X.\" IsOn
  125. X.\"
  126. X.\" Dd    distance to space vertically before a "display"
  127. X.\" These are what n/troff use for interparagraph distance
  128. X.\"-------
  129. X.if t .nr Dd .4v
  130. X.if n .nr Dd 1v
  131. X.\"-------
  132. X.\" Sp    space down the interparagraph distance
  133. X.\"-------
  134. X.de Sp
  135. X.sp \\n(Ddu
  136. X..
  137. X.\"-------
  138. X.\" Ds    begin a display, indented .5 inches from the surrounding text.
  139. X.\"
  140. X.\" Note that uses of Ds and De may NOT be nested.
  141. X.\"-------
  142. X.de Ds
  143. X.Sp
  144. X.in +0.5i
  145. X.nf
  146. X..
  147. X.\"-------
  148. X.\" De    end a display (no trailing vertical spacing)
  149. X.\"-------
  150. X.de De
  151. X.fi
  152. X.in
  153. X..
  154. X.TH IsOn 1 "" NCEMRSoft
  155. X.\"-------
  156. X.SH "NAME"
  157. X.\"-------
  158. XIsOn \(em Network user logon monitor
  159. X.\"-------
  160. X.SH "SYNOPSIS"
  161. X.\"-------
  162. X.HP
  163. X.B ison
  164. X.RI [ "\-bdFIjLmoq" ]
  165. X.RI [ "\-p" "\ "\c
  166. X.BR seconds ]
  167. X.RI [ "\-i" "\ "\c
  168. X.BR num ]
  169. X.RI [ "\-o" "\ "\c
  170. X.BR "outfile" ]
  171. X.RI [ "\-c" "\ "\c
  172. X.BR "command" ]
  173. X.RI [ "\-f" "\ "\c
  174. X.BR "userlist" ]
  175. X.RI [ "usernames..." ]
  176. X.\"-------
  177. X.SH "DESCRIPTION"
  178. X.\"-------
  179. X.PP
  180. XYou can use
  181. X.I IsOn
  182. Xto detect when a certain user logs on, and optionally run another program
  183. Xor shell script in response.
  184. X.I IsOn
  185. Xcan detect when a user becomes active (i.e. has no idle time) or logs off.
  186. XYou can use
  187. X.I IsOn
  188. Xto continually monitor a user's logons and logoffs, if you just want to
  189. Xknow when someone is online.
  190. X.PP
  191. X.I IsOn
  192. Xcan do all of this with multiple users and multiple remote or local hosts.
  193. XFor the local machine,
  194. X.I IsOn
  195. Xmonitors the
  196. X.I utmp
  197. Xfile for optimal performance. For the remote hosts,
  198. X.I IsOn
  199. Xtries to use the
  200. X.I rusers
  201. Xfunction from the
  202. X.I Remote Procedure Call Services
  203. Xlibrary if it is available for optimal perfomance and minimal network
  204. Xtraffic. Otherwise it can use the
  205. X.I finger
  206. Xprogram as plan B.
  207. X.PP
  208. X.I IsOn
  209. Xlowers it's priority automatically, so it takes very little CPU time, and
  210. Xspares you the trouble of remembering to use
  211. X.IR "nice" "(1)."
  212. XIt also puts itself in the background automatically.
  213. X.\"-------
  214. X.SH "OPTIONS"
  215. X.\"-------
  216. X.TP
  217. X.B \-b
  218. XToggles beeping on important messages the program prints.
  219. X.TP
  220. X.B \-d
  221. XPrints debugging information during operation.
  222. X.TP
  223. X.B \-F
  224. XTells the program not to bother trying
  225. X.I rusers
  226. Xfor the given remote hosts and try
  227. X.I finger
  228. Xinstead. By default the program tries
  229. X.IR rusers,
  230. Xand if the remote host does not support
  231. X.IR RPC,
  232. Xit then falls back to
  233. X.IR finger.
  234. X.TP
  235. X.BI \-i " x"
  236. XSets the maximum number of iterations before giving up to
  237. X.I x.
  238. XBy default the program doesn't give up until all users have been detected
  239. Xor you log out.
  240. X.TP
  241. X.B \-I
  242. XTells the program to keep polling the given users until they have no idle
  243. Xtime. If you supply this option,
  244. X.I IsOn
  245. Xwill print a message when the person logs on, and print another message
  246. Xlater when the user becomes active.
  247. X.TP
  248. X.B \-j
  249. XThis toggles daemon behavior. Depending on how the program was compiled, this
  250. Xwill allow you do the opposite. If the default is to go into the background,
  251. Xsupplying this option tells the program not to go into the background, and
  252. Xvice-versa.
  253. X.TP
  254. X.B \-L
  255. XThis tells the program not to exit when you log off. Normally the program
  256. Xterminates when all the given users have logged on, or you log off, since you
  257. Xwould not be online to read the logon notices. This option is usually used
  258. Xwith the
  259. X.B "\-c"
  260. Xoption, described below.
  261. X.TP
  262. X.B \-m
  263. XSupplying this option makes the program keep monitoring all users, telling
  264. Xyou each time any of them log on or off. This feature is similar to the
  265. X.I tcsh
  266. Xshell's
  267. X.I watch
  268. Xvariable, except that
  269. X.I IsOn
  270. Xactually works (but
  271. X.I tcsh
  272. Xis a great shell nonetheless).
  273. X.TP
  274. X.BI \-o " x"
  275. XSpecifies that output should be written to the file
  276. X.I x
  277. Xinstead of to your screen.
  278. X.TP
  279. X.BI \-p " x"
  280. XSets the pause between iterations to
  281. X.I x
  282. Xseconds.
  283. X.TP
  284. X.B \-q
  285. XQuiet mode, no output at all will be written.  Usually used with the
  286. X.B "\-c"
  287. Xoption, described below.
  288. X.TP
  289. X.BI \-c " command"
  290. XThis tells the program to execute
  291. X.I command
  292. Xeach time a user logs on. In addition, the program also expands certain
  293. Xflags within
  294. X.I command
  295. Xbefore executing:
  296. X.RS
  297. X.TP
  298. X.BR "%h" " inserts the name of the user's host machine."
  299. X.TP
  300. X.BR "%i" " inserts user's idletime, or zero if not idle."
  301. X.TP
  302. X.BR "%m" " inserts which was polling method was used."
  303. X.TP
  304. X.BR "%t" " inserts the user's tty name."
  305. X.TP
  306. X.BR "%u" " inserts the user's username."
  307. X.TP
  308. X.BR "%U" " inserts the user's complete user@host address."
  309. X.RE
  310. X.TP
  311. X.BI \-f " file"
  312. XThis tells the program to read
  313. X.I file
  314. Xto get the list of users. Besides convenience, as an added benefit you can
  315. Xhave the program perform differently for each user, instead of having the
  316. Xcommand line options apply to all users.
  317. X.RS
  318. X.PP
  319. XEach line in the file should contain one username, a set of user flags, and
  320. Xthe command to execute when this user logs on. The exact format is a username
  321. Xfollowed by whitespace, followed by the user flags, followed by whitespace,
  322. Xand the command which continues until the end of the line. The command itself
  323. Xcan contain whitespace and percent flags to be expanded. You may omit either
  324. Xthe user flags, command, or both if you wish.
  325. X.PP
  326. XThere are only three user flags as of this writing, and 
  327. Xthey are
  328. X.BR "\-F" ", " "\-I" ", and "\-m"
  329. Xwhich perform as described above, except that they apply only to the
  330. Xspecifed user. If you do not want to use any user flags,
  331. Xjust use a plain ``\-'' to signify no flags at all. See below for an
  332. Xexample which will explain everything.
  333. X.RE
  334. X.PP
  335. XIf you do not supply a userfile, you can type the users on the command
  336. Xline along with the options, or do neither and you will be prompted for
  337. Xa single user by the program (handy if you don't want that to show up
  338. Xin
  339. X.IR "ps" " or " "w" ")."
  340. X.PP
  341. XThe usernames themselves can be in two formats. If you give a complete
  342. X``username@remote.host'' type specification, the program assumes that is not
  343. Xthe machine you are on and tries the remote methods. Otherwise you can
  344. Xjust give ``username'' to tell the program to search the same machine you
  345. Xare on.
  346. X.\"-------
  347. X.SH "SAMPLE USER FILE"
  348. X.\"-------
  349. X.nf
  350. Xtmcmille@scott.skidmore.edu   \-IF  date >> /users/me/Tara.log
  351. Xpdietz                        \-m   /usr/bsd/w | fgrep %u | mail me
  352. Xshari@cse.unl.edu             \-
  353. Xspyros                        \-    write %u %t < /users/me/memo
  354. X.fi
  355. X.\"-------
  356. X.SH "EXAMPLES"
  357. X.\"-------
  358. X.PP
  359. X.nf
  360. Xison mgleason
  361. Xison -I mgleason
  362. Xison -dF -i 100 -p 60 tmcmille@scott.skidmore.edu
  363. Xison -c "echo Tara is on %t." tmcmille@scott.skidmore.edu
  364. Xison -f userfile
  365. Xison -o pdietz humphrey brooke hgleason pnellor enellor kera bretn
  366. X.fi
  367. X.PP
  368. XNote that it doesn't matter how many users you track, but the number of
  369. Xhosts does. The more hosts, the more network traffic. Also, the longer
  370. Xyou set the iteration delay, the better.
  371. X.\"-------
  372. X.SH "BUGS"
  373. X.\"-------
  374. X.PP
  375. XIt is possible that a user could both log on and off while
  376. X.I IsOn
  377. Xis sleeping between tries. Similarly, it is also possible when you've
  378. Xspecified to poll users until they become active, to have an idle user
  379. Xsuddenly activate just long enough to log out while
  380. X.I IsOn
  381. Xsleeps.
  382. X.PP
  383. XWhen
  384. X.I IsOn
  385. Xis using
  386. X.I finger
  387. Xas it's remote polling method, sometimes it will not be able to determine
  388. Xa user's idle time or TTY because of a non-standard remote
  389. X.I finger
  390. Xserver.
  391. X.\"-------
  392. X.SH "FILES"
  393. X.\"-------
  394. X.PP
  395. X.I /etc/utmp
  396. X.\"-------
  397. X.SH "AUTHORS"
  398. X.\"-------
  399. X.PP
  400. XThe original concept and first and second versions by
  401. X.IR "Phil Dietz" " of " "NCEMRSoft"
  402. X(pdietz@cse.unl.edu);
  403. X.PP
  404. XAll subsequent versions, including this one, by 
  405. X.IR "Mike Gleason" " of " "NCEMRSoft"
  406. X(mgleason@cse.unl.edu);
  407. X.PP
  408. X.RI "Copyright 1990\-94 by " "NCEMRSoft" "."
  409. X.\"-------
  410. X.SH "SEE ALSO"
  411. X.\"-------
  412. X.HP
  413. X.IR "RFC 1288" ", ``The Finger User Information Protocol''"
  414. X.HP
  415. X.IR "RFC 1057" ", ``RPC: Remote Procedure Call Protocol Specification: Version 2''"
  416. X.HP
  417. X.IR "rusers" "(3),"
  418. X.IR "finger" "(1),"
  419. X.IR "rusers" "(1),"
  420. X.IR "nice" "(1),"
  421. X.IR "nice" "(2),"
  422. X.IR "ps" "(1),"
  423. X.IR "w" "(1),"
  424. X.IR "who" "(1),"
  425. X.IR "write" "(1),"
  426. X.IR "talk" "(1),"
  427. X.IR "tcsh" "(1),"
  428. X.IR "ncftp" "(1)."
  429. X.\" end of file
  430. END_OF_FILE
  431. if test 7904 -ne `wc -c <'ison.1'`; then
  432.     echo shar: \"'ison.1'\" unpacked with wrong size!
  433. fi
  434. # end of 'ison.1'
  435. fi
  436. if test -f 'config.h' -a "${1}" != "-c" ; then 
  437.   echo shar: Will not clobber existing file \"'config.h'\"
  438. else
  439. echo shar: Extracting \"'config.h'\" \(4199 characters\)
  440. sed "s/^X//" >'config.h' <<'END_OF_FILE'
  441. X/* IsOn's config.h */
  442. X
  443. X/* Change what you like.  Everything is surrounded in #ifndef blocks,
  444. X * so you can just add them to the command line if you like, such
  445. X * as -DBEEP=0.  Note that you should not comment out the whole #define
  446. X * line.  The code uses #if's and not #ifdef's so just change a 1 to a
  447. X * 0, etc., instead of #undef'ing the symbol.
  448. X *
  449. X * After browsing this whole file, and making the changes needed for your
  450. X * system, it should compile cleanly.  If not, re-edit this file, and
  451. X * make sure you have your compiler and flags set up correctly in the
  452. X * Makefile.
  453. X */
  454. X
  455. X/* Define this 1 if you want to use RPC's rusers() call (highly
  456. X * recommended).  Otherwise 0.
  457. X */
  458. X#ifndef RPC
  459. X#define RPC 1    /* 1 or 0 */
  460. X#endif
  461. X
  462. X/* For best results, define FINGER to the full path leading to your
  463. X * finger executable.  If you don't, we'll have to hope that the path
  464. X * is in the user's PATH.
  465. X */
  466. X#ifndef FINGER
  467. X#define FINGER "finger"
  468. X#endif
  469. X
  470. X/* If you define DAEMON to be 1, then by default, ison is a daemon,
  471. X * and the user must use -j to prevent ison from going into the back-
  472. X * ground by default.  If you define DAEMON to 0, then the user must
  473. X * supply -j to have ison go into the background by itself, and runs
  474. X * as a normal program.
  475. X */
  476. X#ifndef DAEMON
  477. X#define DAEMON 1                /* Daemon by default? (1 or 0) */
  478. X#endif
  479. X
  480. X/* Do you want ison processes to nice themselves?
  481. X * I recommend it, since ison is usually a less important process.
  482. X */
  483. X#ifndef NICE
  484. X#define NICE 1        /* 1 or 0 */
  485. X#endif
  486. X
  487. X/* Do you mind if the program uses ^G's by default to get your attention?
  488. X * The user can use -b to not do the default of what you choose here. */
  489. X#ifndef BEEP
  490. X#define BEEP 1        /* 1 or 0 */
  491. X#endif
  492. X
  493. X/* Do you have strchr()?  More than likely you do, unless you're running
  494. X * an older BSD system that still only has index() (the BSD equivalent)
  495. X * and not strchr too
  496. X */
  497. X#ifndef HAVE_STRCHR
  498. X#define HAVE_STRCHR 1
  499. X#endif
  500. X
  501. X/* You should only define this to 0 if you don't have the strstr()
  502. X * call.  We can do without it, but you won't be able to detect
  503. X * the idle time when using Finger.
  504. X */
  505. X#ifndef HAVE_STRSTR
  506. X#define HAVE_STRSTR 1    /* 1 or 0 */
  507. X#endif
  508. X
  509. X/* Similarly, if you don't have both strftime() AND localtime()
  510. X * stick a 0 here.  We can work around this too.
  511. X */
  512. X#ifndef HAVE_STRFTIME
  513. X#define HAVE_STRFTIME 1
  514. X#endif
  515. X
  516. X/* Do you have <stdlib.h>?  Most recent systems do. */
  517. X#ifndef HAVE_STDLIBH
  518. X#define HAVE_STDLIBH 1
  519. X#endif
  520. X
  521. X/* Similarly, do you have <stdarg.h>?  If you define HAVE_STDARGH to 0,
  522. X * we'll try <varargs.h> instead.  Older versions of SunOS will need
  523. X * <varargs.h>
  524. X */
  525. X#ifndef HAVE_STDARGH
  526. X#define HAVE_STDARGH 1
  527. X#endif
  528. X
  529. X/* You can enable some extra debugging messages if you set this to
  530. X * 1, or save a few bytes and leave it 0.
  531. X */
  532. X#ifndef DEBUG
  533. X#define DEBUG 0        /* 0 or 1 */
  534. X#endif
  535. X
  536. X/* These are the two checks ison tries to see if you've logged off so ison
  537. X * can kill itself.  I usually use both of them, but if one or both don't
  538. X * work correctly use undef here.
  539. X */
  540. X#ifndef CHECK_PARENT
  541. X#define CHECK_PARENT    1    /* (1 or 0) check to see if our parent is alive */
  542. X#endif
  543. X
  544. X#ifndef CHECK_STDERR
  545. X#define CHECK_STDERR 1    /* (1 or 0) check to see if stderr is a tty */
  546. X#endif
  547. X
  548. X#ifndef DEFAULT_LOCAL_SLEEP
  549. X#define DEFAULT_LOCAL_SLEEP 10   /* seconds to sleep between Utmp()'s */
  550. X#endif
  551. X
  552. X#ifndef DEFAULT_REMOTE_SLEEP
  553. X#define DEFAULT_REMOTE_SLEEP 45  /* secs to sleep between Finger/RUsers */
  554. X#endif
  555. X
  556. X/* You can put an actual number for this one, but I recommend leaving
  557. X * it a -1L.  This way it will poll forever until you logoff, it finds
  558. X * everyone, or the user supplies the max iterations.
  559. X */
  560. X#ifndef MAXITER
  561. X#define MAXITER (-1L)            /* Default is -1L */
  562. X#endif
  563. X
  564. X/* You can define a default command, but I don't recommend it.  This
  565. X * command will be executed each time a user is found, unless the
  566. X * user who ran the program supplied their own command instead.
  567. X */
  568. X#ifndef COMMAND
  569. X#define COMMAND NULL            /* Default is NULL */
  570. X#endif
  571. X
  572. X/* If you're debugging the program (then you're probably me), you
  573. X * may want to use a debugging malloc library to help out.
  574. X */
  575. X#ifndef DBMALLOC
  576. X#define DBMALLOC 0    /* (0 or 1) Linking w/ a debugging malloc library? */
  577. X#endif
  578. X
  579. X/* eof config.h */
  580. END_OF_FILE
  581. if test 4199 -ne `wc -c <'config.h'`; then
  582.     echo shar: \"'config.h'\" unpacked with wrong size!
  583. fi
  584. # end of 'config.h'
  585. fi
  586. if test -f 'ison.h' -a "${1}" != "-c" ; then 
  587.   echo shar: Will not clobber existing file \"'ison.h'\"
  588. else
  589. echo shar: Extracting \"'ison.h'\" \(5825 characters\)
  590. sed "s/^X//" >'ison.h' <<'END_OF_FILE'
  591. X/* ison.h */
  592. X
  593. X#define VERSION_STR "Version 5.0 (December 7, 1993)"
  594. X
  595. X/* IsOn is Copyright 1990-1994 by NCEMRSoft. */
  596. X
  597. X#define SZ(expr) ((size_t) (expr))
  598. X
  599. X/* Various ways to tweak PrintF(): */
  600. X#define DEBUG_MSG       001
  601. X#define NO_HEADER_MSG   002
  602. X#define NO_BEEP_MSG     004
  603. X#define FLUSH_MSG       010
  604. X
  605. X/* Arbitrary "hostname" for this machine.  But it usually works if you use
  606. X * it as a hostname anyway.
  607. X */
  608. X#define ISLOCALHOST "localhost"    
  609. X
  610. X#define NOT_IDLE (0)   /* User is active, and has no idletime. */
  611. X#define IDLETIME_UNKNOWN (-1)  /* We can't determine if idle or not. */
  612. X#define IS_IDLE (-2)   /* User is idle, but don't know exact #minutes. */
  613. X
  614. X/* This is used to tell the program there is no maximum number of
  615. X * iterations.
  616. X */
  617. X#define NO_LIMIT (-1L)
  618. X
  619. X/* You can have comment lines in the data file.  Lines starting with
  620. X * this character are skipped.
  621. X */
  622. X#define COMMENT_CHAR '#'
  623. X
  624. X/* Return codes for exit(): */
  625. X#define EXIT_SUCCESSFUL                0
  626. X#define EXIT_MAX_ITERATIONS            1
  627. X#define EXIT_NOT_LOGGED_IN            2
  628. X#define EXIT_USAGE                    3
  629. X#define EXIT_PARENT                    4
  630. X#define EXIT_FATAL_ERROR            (-1)
  631. X
  632. X#ifndef UTMP_FILE                      /* Most define this in utmp.h;  SunOS
  633. X                                       * 4.1.1 doesn't. */
  634. X#   define UTMP_FILE "/etc/utmp"
  635. X#endif
  636. X
  637. X#ifndef INDEX
  638. X#   if HAVE_STRCHR
  639. X#       define INDEX strchr            /* ANSI, System V */
  640. X#       define RINDEX strrchr
  641. X#   else
  642. X#       define INDEX index            /* BSD */
  643. X#       define RINDEX rindex
  644. X#   endif
  645. X#endif
  646. X
  647. X#define DONEWITHHOST(hp) hp->usersDone = hp->nUsers
  648. X#define CALLOC1(siz) calloc((size_t)1, siz)
  649. X
  650. Xtypedef struct User *UserPtr;
  651. Xtypedef struct User {
  652. X    UserPtr next;
  653. X    int idlemsg;            /* Only say this user is idle one time. */
  654. X    time_t tyme;            /* This user's login date, if we know it. */
  655. X    int idletime;            /* Idle time of user, in minutes (maybe). */
  656. X    int wasOn;                /* If the user was on before the current iter. */
  657. X    int wasIdle;            /* If user was idle for current iter. */
  658. X    int isOn;                /* If this user is on for the current iter. */
  659. X    int logons;                /* Mostly for debugging. */
  660. X    int pollUntilActive;    /* Wait until user is not idle? */
  661. X    int detectLogoffs;        /* Monitor logoffs too? */
  662. X    char *cmd;                /* What to do if this person is on. */
  663. X    char username[12];        /* This user's login name. */
  664. X    char dev[12];            /* This user's tty. */
  665. X    char idlestr[8];        /* only used by Finger. */
  666. X} User;
  667. X
  668. Xtypedef struct Host *HostPtr;
  669. X
  670. X/* This may cause some compilation problems :-( */
  671. X#ifdef ansi
  672. Xtypedef void (*pollproc_t)(IsonParams *g, HostPtr hp);
  673. X#else
  674. Xtypedef int (*pollproc_t)();
  675. X#endif
  676. X
  677. Xtypedef struct Host {
  678. X    HostPtr next;
  679. X    char *hostname;            /* FQDN name of the this host, if remote. */
  680. X    pollproc_t pollproc;    /* Which polling routine in use. */
  681. X    UserPtr firstUser;        /* List of users on this machine to look for. */
  682. X    UserPtr lastUser;
  683. X    size_t nUsers;            /* Number of users in list. */
  684. X    size_t usersDone;        /* Number we've detected already. */
  685. X    int idleCol;            /* For Finger();  where to look for idle time. */
  686. X    int ttyCol;                /* Same, but for the TTY. */
  687. X} Host;
  688. X
  689. X/* This program started out by not using any global variables.  I kept
  690. X * the tradition alive by using this trick.  We keep one of the following
  691. X * structures on the stack, and then pass a pointer to it around to the
  692. X * various sub-routines.  That way this program is re-entrant if you
  693. X * know how to compile it that way.  Of course the tradeoff is that there
  694. X * are going to be plenty of extra pointer dereferences.
  695. X */
  696. Xtypedef struct IsonParams {
  697. X    FILE *outfile;            /* Stream to print messages on. */
  698. X    char *progname;            /* Short name of this program. */
  699. X    long maxIters;            /* An upperbound, if any, on iterations. */
  700. X    long iter;                /* The current iteration number. */
  701. X    int sleep;                /* How long to delay between iterations. */
  702. X    int daemon;                /* Are we a background process? */
  703. X    int debug;                /* Printing diagnostic output? */
  704. X    int canBeep;            /* Printing ^G's with important messages? */
  705. X    int memInUse;            /* Dynamic memory in use; used to detect leaks. */
  706. X    int pid;                /* PID of this process. */
  707. X    int parentPid;            /* The parent PID of ison. */
  708. X    int stdinatty;            /* Were we run from a shell script? */
  709. X    int pollmsg;            /* Print startup message? */
  710. X    int autodie;            /* exit() when you logoff? */
  711. X    int detectLogoffs;        /* Staying on, reporting log ons/offs? */
  712. X    FILE *utmpfp;            /* Used by Utmp() for localhost's utmp file. */
  713. X    int nHosts;                /* How many hosts we're polling. */
  714. X    int nRemoteHosts;        /* How many of them are remote, if any. */
  715. X    int hostsDone;            /* How many are totally finished. */
  716. X    HostPtr firstHost;        /* The host list. */
  717. X    HostPtr lastHost;
  718. X} IsonParams;
  719. X
  720. Xextern int optind;            /* getopt() stuff */
  721. Xextern char *optarg;
  722. X
  723. Xextern int errno;
  724. X
  725. X#if defined(__STDC__) || defined(__cplusplus) || defined(__EXTENSIONS__)
  726. X#define Pr(s) s
  727. X#define ansi 1
  728. X#include <unistd.h>    /* just for prototypes... can be omitted. */
  729. X#ifndef __EXTENSIONS__
  730. Xextern FILE *   popen(const char *, const char *);
  731. X#endif
  732. X#else
  733. X#define Pr(s) ()
  734. X#define ansi 0
  735. Xextern FILE *popen();
  736. X#endif
  737. X
  738. X/* Function prototypes... */
  739. Xint Strncasecmp Pr((char *a, char *b, size_t n));
  740. Xvoid DoneWithUser Pr((IsonParams *g, HostPtr hp, UserPtr up));
  741. Xvoid RunCommand Pr((HostPtr hp, UserPtr up));
  742. Xvoid MakeSureIAmLoggedIn Pr((IsonParams *g));
  743. Xchar *UserAddress Pr((char *buf, HostPtr hp, UserPtr up));
  744. Xchar *TimeStr Pr((char *buf, size_t siz, time_t t));
  745. Xvoid Delay Pr((IsonParams *g));
  746. Xvoid IterationDebugMsg Pr((IsonParams *g, char *whichproc, HostPtr hp));
  747. Xvoid UserIsIdle Pr((IsonParams *g, HostPtr hp, UserPtr up));
  748. Xvoid Utmp Pr((IsonParams *g, HostPtr hp));
  749. Xvoid Finger Pr((IsonParams *g, HostPtr hp));
  750. Xvoid RUsers Pr((IsonParams *g, HostPtr hp));
  751. Xint AddUser Pr((IsonParams *g, char *username, int F, int noIdle, int mon, char *cmd));
  752. Xvoid Poll Pr((IsonParams *g));
  753. Xvoid ReadDataFile Pr((IsonParams *g, char *fname));
  754. Xvoid Usage Pr((IsonParams *g));
  755. X
  756. X/* eof ison.h */
  757. END_OF_FILE
  758. if test 5825 -ne `wc -c <'ison.h'`; then
  759.     echo shar: \"'ison.h'\" unpacked with wrong size!
  760. fi
  761. # end of 'ison.h'
  762. fi
  763. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  764.   echo shar: Will not clobber existing file \"'Makefile'\"
  765. else
  766. echo shar: Extracting \"'Makefile'\" \(2144 characters\)
  767. sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  768. X# IsOn's Makefile
  769. X#
  770. X# As usual comment lines are those beginning with a # character;  I've
  771. X# left some extra comments in there to give more example values for some
  772. X# Make variables.
  773. X
  774. X# Your favorite C Compiler, and flags.
  775. XCC=cc
  776. X#CC=gcc
  777. X
  778. XCFLAGS=-O
  779. X#CFLAGS=-O2
  780. X#CFLAGS=-g
  781. X#CFLAGS=-ansi -fullwarn -woff 211,269,270,303,309 -g -I.. -L..
  782. X#CFLAGS=-g -Wall -Wshadow -Wconversion -Wstrict-prototypes -Wmissing-prototypes
  783. X
  784. X
  785. X# Link flags -- add the RPC library(s) here if you want to use it.
  786. X#
  787. X# For SunOS, you just need -lrpcsvc.  That may do it for most systems.
  788. X#
  789. X# My system needs to libraries, because the librpcsvc library relies on
  790. X#   functions not included in libc (it needs the XDR routines).
  791. X#   So I add -lrpcsvc -lsun here to get it to link correctly.
  792. X#
  793. X# If you aren't using RPC, you can just set LFLAGS to empty.
  794. X
  795. X
  796. XLFLAGS=-lrpcsvc
  797. X#LFLAGS=-lrpcsvc -lsun
  798. X#LFLAGS=-lrpcsvc -lsun -lc_s
  799. X#LFLAGS=
  800. X
  801. X
  802. X# Set STRIP to -s if you want a much smaller binary (stripped of debugging
  803. X#   symbols).
  804. X
  805. XSTRIP=-s
  806. X#STRIP=
  807. X
  808. X
  809. X# Definitions.  EDIT config.h!  PLEASE read through that whole file,
  810. X# and make the necessary changes!
  811. X
  812. XDEFS= #-DHAVE_STDARGH=0 -DRPC=1 -DDEBUG=1
  813. X
  814. X
  815. X
  816. X
  817. X### SHOULD NOT NEED TO EDIT BEYOND THIS POINT ###
  818. X
  819. X# Source file name minus .c, and compiled exectuable's pathname.
  820. XPROG=ison
  821. XOFILE=ison
  822. XVERS=5.0
  823. XTARNAME=$(PROG)$(VERS).tar
  824. X
  825. XBLURB=Blurb
  826. XPACKAGE=INSTALL patchlevel.h $(PROG).1 config.h ison.h Makefile $(PROG).c
  827. X
  828. Xall: $(PROG)
  829. X
  830. X$(PROG): $(PROG).c config.h ison.h
  831. X    $(CC) $(CFLAGS) $(DEFS) $(PROG).c -o $(OFILE) $(LFLAGS) $(STRIP)
  832. X    -@ls -l $(OFILE)
  833. X
  834. X# For my system:
  835. Xirix:
  836. X    cc -O3 -DRPC=1 $(PROG).c -o $(OFILE) -lrpcsvc -lsun -lc_s -s
  837. X    @-rm -f *.[ou]
  838. X    @-chmod 755 $(OFILE)
  839. X    @ls -l $(OFILE)
  840. X    mv ./$(OFILE) $(HOME)/bin
  841. X
  842. Xdebug:
  843. X    cc -g -DRPC=1 -DDEBUG=1 $(PROG).c -o $(OFILE) -lrpcsvc -lsun
  844. X
  845. Xshar:
  846. X    shar $(PACKAGE) | cat $(BLURB) - > $(PROG).shar
  847. X
  848. Xtar:
  849. X    ( cd .. ; tar cvf ./$(PROG).tar ison )
  850. X    mv ../$(TARNAME) .
  851. X    -@ls -l $(TARNAME)
  852. X
  853. Xtar2:
  854. X    tar cvf $(PROG).tar $(PACKAGE)
  855. X
  856. Xgz: tar
  857. X    gzip -v $(TARNAME)
  858. X    -@ls -l $(TARNAME).gz
  859. X
  860. Xfinst: gz
  861. X    cp $(TARNAME).gz /usr/people/ftp/pub/mgleason
  862. X
  863. Xclean:
  864. X    rm -f core $(OFILE)
  865. X
  866. Xclobber: clean
  867. X    rm -i $(PACKAGE)
  868. END_OF_FILE
  869. if test 2144 -ne `wc -c <'Makefile'`; then
  870.     echo shar: \"'Makefile'\" unpacked with wrong size!
  871. fi
  872. # end of 'Makefile'
  873. fi
  874. if test -f 'ison.c' -a "${1}" != "-c" ; then 
  875.   echo shar: Will not clobber existing file \"'ison.c'\"
  876. else
  877. echo shar: Extracting \"'ison.c'\" \(30133 characters\)
  878. sed "s/^X//" >'ison.c' <<'END_OF_FILE'
  879. X/* IsOn... Copyright 1990-94 NCEMRSoft.  Use at your own risk.
  880. X * This version by Mike Gleason, NCEMRSoft (mgleason@cse.unl.edu).
  881. X * Original version by Phil Dietz, NCEMRSoft (pdietz@cse.unl.edu).
  882. X */
  883. X
  884. X#include <sys/types.h>
  885. X#include <sys/time.h>
  886. X#include <sys/stat.h>
  887. X#include <utmp.h>
  888. X#include <stdio.h>
  889. X#include <string.h>
  890. X#include <ctype.h>
  891. X#include <signal.h>
  892. X
  893. X#include "config.h"
  894. X#include "ison.h"
  895. X
  896. X#if HAVE_STDLIBH
  897. X#include <stdlib.h>
  898. X#else
  899. Xextern void *malloc(), *calloc();
  900. X#endif
  901. X
  902. X#if HAVE_STDARGH
  903. X#include <stdarg.h>
  904. X#else
  905. X#include <varargs.h>
  906. X#endif
  907. X
  908. X#if RPC
  909. X#include <sys/socket.h>
  910. X#include <rpc/rpc.h>
  911. X#include <rpcsvc/rusers.h>
  912. X#endif    /* RPC */
  913. X
  914. X#if DBMALLOC
  915. X#include <malloc.h>
  916. X#endif
  917. X
  918. X#if ansi
  919. Xint Strncasecmp(register char *a, register char *b, register size_t n)
  920. X#else
  921. Xint Strncasecmp(a, b, n)
  922. X     register char *a, *b;
  923. X     register size_t n;
  924. X#endif
  925. X{
  926. X    register int A, B;
  927. X    register int fullcompare;
  928. X    
  929. X    /* You can supply a 0 to mean just do a regular Strcasecmp. */
  930. X    fullcompare = (n == (size_t) 0);
  931. X    while ((fullcompare) || (n-- > (size_t) 0)) {
  932. X        A = islower(*a) ? tolower((int) *a++) : *a++;
  933. X        B = islower(*b) ? tolower((int) *b++) : *b++;
  934. X        if (A > B)
  935. X            return (A - B);
  936. X        if (B > A)
  937. X            return (B - A);
  938. X        if (A == 0 && B == 0)
  939. X            return (0);
  940. X    }
  941. X    return (0);                           /* equal to n characters if we get
  942. X                                        * here */
  943. X}                                       /* Strncasecmp */
  944. X
  945. X
  946. X
  947. X
  948. X#if ansi
  949. Xchar *TimeStr(char *buf, size_t siz, time_t t)
  950. X#else
  951. Xchar *TimeStr(buf, siz, t)
  952. X    char *buf;
  953. X    size_t siz;
  954. X    time_t t;
  955. X#endif
  956. X{
  957. X#if HAVE_STRFTIME
  958. X    char buf2[128];
  959. X
  960. X    if (t == (time_t)0)
  961. X        time(&t);
  962. X    (void) strftime(buf2, SZ(127), "%I:%M %p", localtime(&t));
  963. X    if (buf2[0] == '0' && buf[1] == '0') {
  964. X        buf2[0] = '1';
  965. X        buf2[1] = '2';
  966. X    }
  967. X    (void) strncpy(buf, buf2[0] == '0' ? buf2 + 1 : buf2 , siz);
  968. X#else
  969. X    if (t == (time_t)0)
  970. X        time(&t);
  971. X    (void) strncpy(buf, ctime(&t), siz);
  972. X    if (t == (time_t)0)
  973. X        time(&t);
  974. X    buf[strlen(buf) - 1] = '\0';    /* Get rid of the \n. */
  975. X#endif
  976. X    return (buf);
  977. X}    /* TimeStr */
  978. X
  979. X
  980. X
  981. X
  982. X#if ansi && HAVE_STDARGH
  983. Xstatic void PrintF(IsonParams *g0, int flags0, char *fmt0, ...)
  984. X#else
  985. X#if HAVE_STDARGH
  986. Xstatic void PrintF(g0, flags0, fmt0, ...)
  987. X    IsonParams *g0;
  988. X    int flags0;
  989. X    char *fmt0;
  990. X#else
  991. Xstatic void PrintF(va_alist)
  992. X    va_dcl
  993. X#endif    /* !HAVE_STDARGH*/
  994. X#endif    /* !ansi */
  995. X{
  996. X    va_list ap;
  997. X    char tmstr[40];
  998. X    IsonParams *g;
  999. X    int flags;
  1000. X    char *fmt;
  1001. X
  1002. X#if (HAVE_STDARGH == 0)
  1003. X    va_start(ap);
  1004. X    g = va_arg(ap, IsonParams *);
  1005. X    flags = va_arg(ap, int);
  1006. X    fmt = va_arg(ap, char *);
  1007. X#else
  1008. X    va_start(ap, fmt0);
  1009. X    g = g0; flags = flags0; fmt = fmt0;
  1010. X#endif
  1011. X
  1012. X    if (g->outfile == NULL)
  1013. X        return;
  1014. X#if DEBUG
  1015. X    if (((flags & DEBUG_MSG) != 0) && (!g->debug))
  1016. X        return;
  1017. X#endif
  1018. X    if ((flags & FLUSH_MSG) != 0) {
  1019. X        (void) fflush(g->outfile);
  1020. X        return;
  1021. X    }
  1022. X
  1023. X    if ((flags & NO_HEADER_MSG) == 0) {
  1024. X        tmstr[0] = '\0';
  1025. X        if (g->outfile != stderr) {        /* Log file. */
  1026. X            tmstr[0] = ' ';
  1027. X            (void) TimeStr(tmstr+1, sizeof(tmstr)-1, (time_t) 0);
  1028. X        }    
  1029. X        (void) fprintf(g->outfile, "\r\n%s%s%s IsOn: ",
  1030. X            (((flags & (NO_BEEP_MSG|DEBUG_MSG)) != 0) ||
  1031. X                (g->canBeep == 0)) ? "" : "\007",
  1032. X            ((flags & DEBUG_MSG) != 0) ? "#DB#" : "**",
  1033. X            tmstr
  1034. X        );
  1035. X    }
  1036. X    
  1037. X    (void) vfprintf(g->outfile, fmt, ap);
  1038. X    va_end(ap);
  1039. X}    /* PrintF */
  1040. X
  1041. X
  1042. X#if ansi
  1043. Xvoid MakeSureIAmLoggedIn(IsonParams *g)
  1044. X#else
  1045. Xvoid MakeSureIAmLoggedIn(g)
  1046. X    IsonParams *g;
  1047. X#endif
  1048. X{
  1049. X    if (g->autodie) {
  1050. X#if CHECK_PARENT
  1051. X        /* Don't kill ourself if stdin was not a tty in the first place,
  1052. X         * which means we were probably called from a shell script, and
  1053. X         * the shell will exit before we finish.
  1054. X         */
  1055. X        if (g->stdinatty && kill(g->parentPid, 0)) {
  1056. X            /* we've lost our shell! */
  1057. X            PrintF(g, 0, "Lost our parent!\n");
  1058. X            exit(EXIT_NOT_LOGGED_IN);
  1059. X        }
  1060. X#endif
  1061. X#if CHECK_STDERR
  1062. X        if (!isatty(2)) {
  1063. X            /* Hmm... wonder where this is going? */
  1064. X            PrintF(g, 0, "Stderr is not a tty!\n");
  1065. X            exit(EXIT_NOT_LOGGED_IN);
  1066. X        }
  1067. X#endif
  1068. X    }
  1069. X}    /* MakeSureIAmLoggedIn */
  1070. X
  1071. X
  1072. X
  1073. X#if ansi
  1074. Xvoid Delay(IsonParams *g)
  1075. X#else
  1076. Xvoid Delay(g)
  1077. X    IsonParams *g;
  1078. X#endif
  1079. X{
  1080. X    unsigned int sl;
  1081. X
  1082. X    /* Kill some time so more important processes can run. */
  1083. X    g->iter++;
  1084. X#if DEBUG
  1085. X#if DBMALLOC
  1086. X    {
  1087. X        int inuse = (int) malloc_inuse(NULL);
  1088. X        if (inuse > g->memInUse) {
  1089. X            g->memInUse = inuse;
  1090. X            PrintF(g, DEBUG_MSG, "malloc_inuse: %d\n", inuse);
  1091. X        }
  1092. X    }
  1093. X#endif
  1094. X#endif
  1095. X    if ((g->maxIters > 0L) && (g->iter > g->maxIters)) {
  1096. X        PrintF(g, 0, "Giving up after %ld iterations.\n", g->maxIters);
  1097. X        exit(EXIT_MAX_ITERATIONS);
  1098. X    }
  1099. X    if (g->iter == 1L) {
  1100. X        if (g->stdinatty && g->pollmsg) {
  1101. X            /* Print a message if you ran us from an interactive shell. */
  1102. X            PrintF(g, NO_BEEP_MSG, "Polling.");
  1103. X            if (g->daemon)
  1104. X                PrintF(g, NO_HEADER_MSG, "  Type \"kill %d\" to terminate.\n", g->pid);
  1105. X            else
  1106. X                PrintF(g, NO_HEADER_MSG, "..\n");
  1107. X        }
  1108. X    } else {
  1109. X        if (g->sleep > 0) {
  1110. X            /* The user supplied a value to give to sleep. */
  1111. X            sl = (unsigned int) g->sleep;
  1112. X        } else if (g->sleep < 0) {
  1113. X            /* Use one of the defaults.  If you supplied a remote host,
  1114. X             * wait a little longer, otherwise use the local delay
  1115. X             * which is shorter.
  1116. X             */
  1117. X            sl = (unsigned int) ((g->nRemoteHosts > 0) ? DEFAULT_REMOTE_SLEEP :
  1118. X                DEFAULT_LOCAL_SLEEP);
  1119. X        }
  1120. X#if DEBUG
  1121. X        PrintF(g, DEBUG_MSG, "Sleeping %3d%s",
  1122. X            sl,
  1123. X            (g->daemon == 0) ? ": " : "...\r\n"
  1124. X        );
  1125. X        PrintF(g, FLUSH_MSG, "");
  1126. X#endif
  1127. X        (void) sleep(sl);
  1128. X#if DEBUG
  1129. X        PrintF(g, DEBUG_MSG|NO_HEADER_MSG, "done.\n");
  1130. X#endif
  1131. X    }
  1132. X}    /* Delay */
  1133. X
  1134. X
  1135. X
  1136. X#if DEBUG
  1137. X/* If we have debug mode on, we can print a message to the screen telling
  1138. X * the user that we are getting ready to do another check.
  1139. X */
  1140. X#if ansi
  1141. Xvoid IterationDebugMsg(IsonParams *g, char *whichproc, HostPtr hp)
  1142. X#else
  1143. Xvoid IterationDebugMsg(g, whichproc, hp)
  1144. X    IsonParams *g;
  1145. X    char *whichproc;
  1146. X    HostPtr hp;
  1147. X#endif
  1148. X{
  1149. X    char tmstr[40];
  1150. X
  1151. X    PrintF(g, DEBUG_MSG, "Checking %s on %s (try #%ld) at %s\n",
  1152. X        whichproc, hp->hostname,  g->iter,
  1153. X        TimeStr(tmstr, sizeof(tmstr), (time_t)0)
  1154. X    );
  1155. X}    /* IterationDebugMsg */
  1156. X#else
  1157. X#define IterationDebugMsg(a,b,c)
  1158. X#endif    /* DEBUG */
  1159. X
  1160. X
  1161. X
  1162. X#if ansi
  1163. Xvoid RunCommand(HostPtr hp, UserPtr up)
  1164. X#else
  1165. Xvoid RunCommand(hp, up)
  1166. X    HostPtr hp;
  1167. X    UserPtr up;
  1168. X#endif
  1169. X{
  1170. X    char buf[256];
  1171. X    char str[64];
  1172. X    char *catstr;
  1173. X    char *cp, *dp;
  1174. X    size_t n;
  1175. X
  1176. X    if (up->cmd != NULL) {
  1177. X        for (dp = buf, cp = up->cmd, n = sizeof(buf) - 1; *cp != '\0'; cp++) {
  1178. X            if (*cp == '%') {
  1179. X                ++cp;
  1180. X                switch (*cp) {
  1181. X                    case '\0':
  1182. X                        --cp;
  1183. X                        break;
  1184. X                    case 'h':
  1185. X                        catstr = hp->hostname;
  1186. X                        goto cat;
  1187. X                    case 'i':
  1188. X                        if (up->idletime != IS_IDLE) {
  1189. X                            (void) sprintf(str, "%d",
  1190. X                                up->idletime == IS_IDLE ? 0 : up->idletime);
  1191. X                            catstr = str;
  1192. X                        } else catstr = up->idlestr;
  1193. X                        goto cat;
  1194. X                    case 'm':
  1195. X                        if (hp->pollproc == (pollproc_t) Utmp)
  1196. X                            catstr = "utmp";
  1197. X                        else if (hp->pollproc == (pollproc_t) Finger)
  1198. X                            catstr = "finger";
  1199. X                        else
  1200. X                            catstr = "rusers";
  1201. X                        goto cat;
  1202. X                    case 'n':
  1203. X                    case 'u':
  1204. X                        catstr = up->username;
  1205. X                        goto cat;
  1206. X                    case 'N':
  1207. X                    case 'U':
  1208. X                        if (hp->hostname != ISLOCALHOST) {
  1209. X                            (void) sprintf(str, "%s@%s", up->username,
  1210. X                                hp->hostname);
  1211. X                            catstr = str;
  1212. X                        } else
  1213. X                            catstr = up->username;
  1214. X                        goto cat;
  1215. X                    case 't':
  1216. X                        catstr = up->dev;
  1217. X                        goto cat;
  1218. X                    cat:
  1219. X                        for (; (n > 0) && (*catstr != '\0'); --n)
  1220. X                            *dp++ = *catstr++;
  1221. X                        break;
  1222. X                    default:
  1223. X                        if (n > 0) {
  1224. X                            --n;
  1225. X                            *dp++ = *cp;
  1226. X                        }
  1227. X                }
  1228. X            } else if (n > 0) {
  1229. X                --n;
  1230. X                *dp++ = *cp;
  1231. X            }
  1232. X        }
  1233. X        *dp = 0;
  1234. X
  1235. X        if (fork() == 0) {
  1236. X            (void) sleep(1);
  1237. X            (void) execlp("/bin/sh", "sh", "-c", buf, NULL);
  1238. X            (void) perror(buf);
  1239. X            exit(EXIT_FATAL_ERROR);    /* Rarely reached... */
  1240. X        }
  1241. X    }
  1242. X}    /* RunCommand */
  1243. X
  1244. X
  1245. X
  1246. X
  1247. X#if ansi
  1248. Xchar *UserAddress(char *buf, HostPtr hp, UserPtr up)
  1249. X#else
  1250. Xchar *UserAddress(buf, hp, up)
  1251. X    char *buf;
  1252. X    HostPtr hp;
  1253. X    UserPtr up;
  1254. X#endif
  1255. X{
  1256. X    (void) strcpy(buf, up->username);
  1257. X    if (Strncasecmp(hp->hostname, ISLOCALHOST, SZ(0)) != 0) {
  1258. X        (void) strcat(buf, "@");
  1259. X        (void) strcat(buf, hp->hostname);
  1260. X    }
  1261. X    return (buf);
  1262. X}    /* UserAddress */
  1263. X
  1264. X
  1265. X
  1266. X
  1267. X#if ansi
  1268. Xvoid UserIsIdle(IsonParams *g, HostPtr hp, UserPtr up)
  1269. X#else
  1270. Xvoid UserIsIdle(g, hp, up)
  1271. X    IsonParams *g;
  1272. X    HostPtr hp;
  1273. X    UserPtr up;
  1274. X#endif
  1275. X{
  1276. X    char uabuf[128];
  1277. X
  1278. X    up->wasIdle = 1;
  1279. X    if (++up->idlemsg == 1) {
  1280. X        PrintF(g, 0, "%s is logged in,\r\n         but has been idle ",
  1281. X            UserAddress(uabuf, hp, up)
  1282. X        );
  1283. X        if (up->idletime > 0)
  1284. X            PrintF(g, NO_HEADER_MSG, "%d minute%s.\n\n",
  1285. X                up->idletime,
  1286. X                (up->idletime > 1 ? "s" : "")
  1287. X            );
  1288. X        else
  1289. X            PrintF(g, NO_HEADER_MSG, "%s.\n\n", up->idlestr);
  1290. X    }
  1291. X}    /* UserIsIdle */
  1292. X
  1293. X
  1294. X
  1295. X
  1296. X#if ansi
  1297. Xvoid DoneWithUser(IsonParams *g, HostPtr hp, UserPtr up)
  1298. X#else
  1299. Xvoid DoneWithUser(g, hp, up)
  1300. X    IsonParams *g;
  1301. X    HostPtr hp;
  1302. X    UserPtr up;
  1303. X#endif
  1304. X{
  1305. X    char tmstr[40];
  1306. X    char useraddr[128];
  1307. X    char TTY[32];
  1308. X    char since[10];
  1309. X
  1310. X    up->isOn = 1;
  1311. X    if (up->wasOn)    /* Yeah, we know already. */
  1312. X        return;
  1313. X
  1314. X    up->logons++;
  1315. X    up->wasOn = 1;
  1316. X
  1317. X    (void) UserAddress(useraddr, hp, up);
  1318. X
  1319. X    if (up->tyme == (time_t)0) {
  1320. X        since[0] = '\0';
  1321. X        /* tmstr will contain the time _here_ after the TimeStr() then. */
  1322. X    } else {
  1323. X        (void) strcpy(since, " since ");
  1324. X    }
  1325. X    (void) TimeStr(tmstr, sizeof(tmstr), up->tyme);
  1326. X
  1327. X    TTY[0] = '\0';
  1328. X    if (up->dev[0] != '\0')
  1329. X        (void) sprintf(TTY, " to %s", up->dev);
  1330. X
  1331. X    /* We don't want to print the idle message after we print this
  1332. X     * next message.
  1333. X     */
  1334. X    ++up->idlemsg;
  1335. X    if (up->idletime > 0) {
  1336. X        PrintF(g, 0,
  1337. X            "%s logged on%s%s%s,\r\n         but has been idle %d min.\n",
  1338. X            useraddr,
  1339. X            TTY,
  1340. X            since,
  1341. X            since[0] == '\0' ? since : tmstr,
  1342. X            up->idletime
  1343. X        ); 
  1344. X    } else if (up->idletime == NOT_IDLE) {
  1345. X        up->wasIdle = 0;
  1346. X        PrintF(g, 0,
  1347. X            "%s logged on%s%s%s\r\n         and is not idle.\n",
  1348. X            useraddr,
  1349. X            TTY,
  1350. X            since,
  1351. X            since[0] == '\0' ? since : tmstr
  1352. X        ); 
  1353. X    } else if (up->idletime == IDLETIME_UNKNOWN) {
  1354. X        /* Probably using Finger.  Since not all finger servers spew
  1355. X         * their output in the same format, we may not be able to find
  1356. X         * out if the user is idle or not.
  1357. X         */
  1358. X        up->wasIdle = 0;
  1359. X        PrintF(g, 0,
  1360. X            "Detected login of %s at %s.\n",
  1361. X            useraddr,
  1362. X            tmstr
  1363. X        ); 
  1364. X    } else if (up->idletime == IS_IDLE) {
  1365. X        /* Again, from Finger.  We don't bother trying to figure out
  1366. X         * the exact number of minutes since there are various ways
  1367. X         * it prints that figure.  We just know it's there is some
  1368. X         * idletime, which is good enough.  Hopefully we won't be
  1369. X         * using Finger very much anyway.  We just tell them the
  1370. X         * exact same thing Finger told us.
  1371. X         */
  1372. X        PrintF(g, 0,
  1373. X            "Detected login of %s%s at %s,\r\n         but has been idle %s.\n",
  1374. X            useraddr,
  1375. X            TTY,
  1376. X            tmstr,
  1377. X            up->idlestr
  1378. X        ); 
  1379. X    }
  1380. X
  1381. X    if (!up->detectLogoffs) {
  1382. X        /* If we are running detect logoffs mode, we want to run forever,
  1383. X         * so we don't want to increment the done-users counter.  If we
  1384. X         * did that, we would exit once all users had logged on at least
  1385. X         * once.  With this mode, we don't want to exit.
  1386. X         */
  1387. X        if (++hp->usersDone == hp->nUsers) {
  1388. X            ++g->hostsDone;
  1389. X        }
  1390. X    }
  1391. X
  1392. X    /* Run a command (script) if the user requested to. */
  1393. X    RunCommand(hp, up);
  1394. X}    /* DoneWithUser */
  1395. X
  1396. X
  1397. X
  1398. X
  1399. X#if ansi
  1400. Xvoid Utmp(IsonParams *g, HostPtr hp)
  1401. X#else
  1402. Xvoid Utmp(g, hp)
  1403. X    IsonParams *g;
  1404. X    HostPtr hp;
  1405. X#endif
  1406. X{
  1407. X    struct utmp info;
  1408. X    int idletime;
  1409. X    struct stat st;
  1410. X    char ttypath[128];
  1411. X    UserPtr up;
  1412. X
  1413. X    /* Open the utmp file, which is a list of all logged on users. */
  1414. X    if ((g->utmpfp == NULL) && ((g->utmpfp = fopen(UTMP_FILE, "r")) == NULL)) {
  1415. X        (void) perror(UTMP_FILE);
  1416. X        DONEWITHHOST(hp);
  1417. X    }
  1418. X    
  1419. X    /* Reset the utmp file and re-read it. */
  1420. X    (void) rewind(g->utmpfp);
  1421. X
  1422. X    IterationDebugMsg(g, "Utmp", hp);
  1423. X
  1424. X    /* Cycle through all 'users' logged in. */
  1425. X    while (fread(&info, SZ(sizeof(info)), SZ(1), g->utmpfp) == SZ(1)) {
  1426. X        /* See if this guy matches any of the users we are looking for. */
  1427. X        for (up = hp->firstUser; up != NULL; up = up->next) {
  1428. X            if (Strncasecmp(up->username, info.ut_name, SZ(8)) == 0) {
  1429. X                /* This user is logged on.  But is the user active? */
  1430. X                (void) time(&up->tyme);
  1431. X    
  1432. X                up->isOn = 1;
  1433. X                (void) strcat(strcpy(ttypath, "/dev/"), info.ut_line);
  1434. X                idletime = IDLETIME_UNKNOWN;
  1435. X                if (stat(ttypath, &st) == 0) {
  1436. X                    idletime = (int) (up->tyme - st.st_mtime) - 0;
  1437. X                    if (idletime < 0) idletime = NOT_IDLE;
  1438. X                    else idletime /= 60;
  1439. X                }
  1440. X    
  1441. X                up->idletime = idletime;
  1442. X                if (up->pollUntilActive && idletime > 0) {
  1443. X                    UserIsIdle(g, hp, up);
  1444. X                } else {
  1445. X                    /* The user is not idle;  use the real login time. */
  1446. X                    up->tyme = info.ut_time;
  1447. X                
  1448. X                    /* Note the tty the user is on. */
  1449. X                    (void) strcpy(up->dev, info.ut_line);
  1450. X
  1451. X                    DoneWithUser(g, hp, up);
  1452. X                }
  1453. X            }
  1454. X        }
  1455. X    }
  1456. X}                                       /* Utmp */
  1457. X
  1458. X
  1459. X
  1460. X
  1461. X#if ansi
  1462. Xvoid Finger(IsonParams *g, HostPtr hp)
  1463. X#else
  1464. Xvoid Finger(g, hp)
  1465. X    IsonParams *g;
  1466. X    HostPtr hp;
  1467. X#endif
  1468. X{
  1469. X    FILE *in;
  1470. X    register char *cp;
  1471. X    int piperesult, pipelines, i;
  1472. X    char buf[160], pipename[128];
  1473. X    UserPtr up;
  1474. X    
  1475. X    (void) strcat(strcpy(pipename, FINGER), " @");
  1476. X    (void) strcat(pipename, hp->hostname);
  1477. X
  1478. X    if ((in = popen(pipename, "r")) == NULL) {
  1479. X        perror(FINGER);
  1480. X        exit(EXIT_FATAL_ERROR);
  1481. X    }
  1482. X
  1483. X    IterationDebugMsg(g, "Finger", hp);
  1484. X
  1485. X    /* Cycle through all 'users' logged in. */
  1486. X    pipelines = 0;
  1487. X    while (fgets(buf, (int) sizeof(buf), in) != NULL) {
  1488. X        pipelines++;
  1489. X
  1490. X#if HAVE_STRSTR
  1491. X        /* We would like to determine if a user is idle if possible.
  1492. X         * Since not all finger daemons format their output the same
  1493. X         * way, we may not be able to get the idle time.  We can
  1494. X         * find it if the other side prints a column header line.
  1495. X         * From my experience almost all of them being with "Login" as
  1496. X         * the very first column, and if there is an idle time column,
  1497. X         * the word "Idle" will appear on that header line.  If that's
  1498. X         * the case, we remember the offset into the line where the
  1499. X         * word "Idle" occurred.  Later when we find a user on our list,
  1500. X         * we look at the offset into the line.  If a user is idle,
  1501. X         * some sort of text will appear usually a number.  If not, it
  1502. X         * should be just whitespace.
  1503. X         */
  1504. X        if (Strncasecmp("Login", buf, SZ(5)) == 0) {
  1505. X            if (hp->idleCol == 0) {
  1506. X                cp = strstr(buf, "Idle");
  1507. X                if (cp != NULL)
  1508. X                    hp->idleCol = (int) (cp - buf);
  1509. X            }
  1510. X
  1511. X            /* Same thing for the TTY. */
  1512. X            if (hp->ttyCol == 0) {
  1513. X                cp = strstr(buf, "TTY");
  1514. X                if (cp != NULL)
  1515. X                    hp->ttyCol = (int) (cp - buf);
  1516. X            }
  1517. X        }
  1518. X#endif    /* HAVE_STRSTR */
  1519. X
  1520. X        /* put a \0 in the first space after the username for Strncasecmp */
  1521. X        cp = buf;
  1522. X        while (*cp && isspace(*cp) == 0)
  1523. X            cp++;
  1524. X        *cp = '\0';
  1525. X        
  1526. X        /* See if this guy matches any of the users we are looking for. */
  1527. X        for (up = hp->firstUser; up != NULL; up = up->next) {
  1528. X            if (Strncasecmp(up->username, buf, SZ(8)) == 0) {
  1529. X                up->tyme = (time_t)0;    /* Don't know login time. */
  1530. X                up->idletime = IDLETIME_UNKNOWN;
  1531. X                if (hp->idleCol != 0) {
  1532. X                    cp = buf + hp->idleCol;
  1533. X                    cp[4] = 0;
  1534. X                    up->idletime = NOT_IDLE;
  1535. X                    for (i=0; i<4; i++)
  1536. X                        if (!isspace(cp[i])) {
  1537. X                            up->idletime = IS_IDLE;
  1538. X                            /* Well, just save the same string finger
  1539. X                             * said for later use.
  1540. X                             */
  1541. X                            while (isspace(*cp) && *cp != '\0')
  1542. X                                cp++;
  1543. X                            (void) strcpy(up->idlestr, cp);
  1544. X                        }
  1545. X                }
  1546. X                if (hp->ttyCol != 0) {
  1547. X                    cp = buf + hp->ttyCol;
  1548. X                    cp[3] = 0;
  1549. X                    while (isspace(*cp) && *cp != '\0')
  1550. X                        cp++;
  1551. X                    if (Strncasecmp(cp, "co", SZ(2)) == 0)
  1552. X                        (void) strcpy(up->dev, "console");
  1553. X                    else
  1554. X                        (void) strcat(strcpy(up->dev, "tty"), cp);
  1555. X                }
  1556. X                up->isOn = 1;
  1557. X                if (up->pollUntilActive && up->idletime == IS_IDLE) {
  1558. X                    UserIsIdle(g, hp, up);
  1559. X                } else {
  1560. X                    DoneWithUser(g, hp, up);
  1561. X                }
  1562. X            }
  1563. X        }
  1564. X    }
  1565. X
  1566. X    piperesult = pclose(in);       /* close pipe */
  1567. X    if (piperesult) {
  1568. X        PrintF(g, 0,
  1569. X            "%sFinger unsuccessful with %s, so no users from it can be polled.\n",
  1570. X            hp->hostname
  1571. X        );
  1572. X        DONEWITHHOST(hp);
  1573. X    }
  1574. X    if (pipelines <= 1) {
  1575. X        /* finger probably puked */
  1576. X        PrintF(g, 0,
  1577. X            "%s did not supply any Finger output, so no users from it can be polled.\n",
  1578. X            hp->hostname
  1579. X        );
  1580. X        DONEWITHHOST(hp);
  1581. X    }
  1582. X}                                       /* Finger */
  1583. X
  1584. X
  1585. X
  1586. X
  1587. X#if RPC
  1588. X
  1589. X#if ansi
  1590. Xvoid RUsers(IsonParams *g, HostPtr hp)
  1591. X#else
  1592. Xvoid RUsers(g, hp)
  1593. X    IsonParams *g;
  1594. X    HostPtr hp;
  1595. X#endif
  1596. X{
  1597. X    struct utmpidlearr uti;
  1598. X    UserPtr up;
  1599. X    int i;
  1600. X
  1601. X    IterationDebugMsg(g, "RUsers", hp);
  1602. X    uti.uia_cnt = 0; uti.uia_arr = 0;
  1603. X
  1604. X    if (rusers(hp->hostname, &uti) != 0) {
  1605. X        if (g->iter == 1) {
  1606. X            /* This remote site probably doesn't support RPC at all, so
  1607. X             * try finger instead.
  1608. X             */
  1609. X#if DEBUG
  1610. X            PrintF(g, DEBUG_MSG, "RPC failed with %s, will try Finger next time.\n",
  1611. X                hp->hostname);
  1612. X#endif
  1613. X        } else {
  1614. X            /* We were able to use RPC at least once, but it isn't working
  1615. X             * anymore.  We can still try using finger, and if that doesn't
  1616. X             * work, we'll have to give up on this host.
  1617. X             */
  1618. X            PrintF(g, 0, "RPC not reliable with %s, falling back to Finger for this host.\n",
  1619. X                hp->hostname);
  1620. X        }
  1621. X        hp->pollproc = (pollproc_t) Finger;
  1622. X        Finger(g, hp);
  1623. X        return;
  1624. X    }
  1625. X
  1626. X/* I prefer to use my own temp variable, but the internal structure names
  1627. X * differ across different versions of RPCSVC :-(
  1628. X */
  1629. X#define R_UTMP(a) ((*uti.uia_arr[i]).ui_utmp)
  1630. X
  1631. X    /* Cycle through each entry in the remote utmp list, and see if any
  1632. X     * of the entries match the users we are looking for.
  1633. X     */
  1634. X    for (i=0; i<uti.uia_cnt; i++) {
  1635. X        for (up = hp->firstUser; up != NULL; up = up->next) {
  1636. X            if (Strncasecmp(up->username, R_UTMP(uti).ut_name,
  1637. X                SZ(8)) == 0)
  1638. X            {
  1639. X                /* We have found one of our users. */
  1640. X                
  1641. X                /* We can print exact login time, unlike Finger. */
  1642. X                up->tyme = R_UTMP(uti).ut_time;
  1643. X
  1644. X                /* We can also get the exact idletime for this user,
  1645. X                 * without any hassle.
  1646. X                 */
  1647. X                up->idletime = (int) uti.uia_arr[i]->ui_idle;
  1648. X
  1649. X                up->isOn = 1;
  1650. X                if (up->pollUntilActive && up->idletime > 0) {
  1651. X                    UserIsIdle(g, hp, up);
  1652. X                } else {
  1653. X                    /* Note the tty the user is on. */
  1654. X                    (void) strcpy(up->dev, R_UTMP(uti).ut_line);
  1655. X                    DoneWithUser(g, hp, up);
  1656. X                }
  1657. X            }
  1658. X        }
  1659. X    }
  1660. X
  1661. X    /* We're done with this chunk, so get rid of it and get a new one
  1662. X     * at the next iteration.
  1663. X     */
  1664. X    xdr_free(xdr_utmpidlearr, &uti);
  1665. X}    /* RUsers */
  1666. X#endif    /* RPC */
  1667. X
  1668. X
  1669. X
  1670. X
  1671. X#if ansi
  1672. Xint AddUser(IsonParams *g, char *username, int F, int noIdle, int mon, char *cmd)
  1673. X#else
  1674. Xint AddUser(g, username, F, noIdle, mon, cmd)
  1675. X    IsonParams *g;
  1676. X    char *username, *cmd;
  1677. X    int F, mon, noIdle;
  1678. X#endif
  1679. X{
  1680. X    HostPtr hp;
  1681. X    UserPtr up;
  1682. X    int isLocal;
  1683. X    char *cp;
  1684. X    char *hostname;
  1685. X    char userandhost[128];
  1686. X
  1687. X    (void) strcpy(userandhost, username);    /* Don't modify original. */
  1688. X    
  1689. X    /*
  1690. X     * Check the username for an @, which would suggest that it is a
  1691. X     * domain-style address.
  1692. X     */
  1693. X    if ((cp = INDEX(userandhost, '@')) != NULL) {
  1694. X        *cp = 0;                /* now will contain only the username. */
  1695. X        hostname = cp + 1;        /* points to the part after the @. */
  1696. X        isLocal = 0;
  1697. X    } else {
  1698. X        isLocal = 1;
  1699. X        hostname = ISLOCALHOST;    /* Give it an arbitrary name, so we can
  1700. X                                 * group all the local users together.
  1701. X                                 */
  1702. X    }
  1703. X
  1704. X    for (hp=g->firstHost; hp != NULL; hp = hp->next) {
  1705. X        if (Strncasecmp(hostname, hp->hostname, 0) == 0) {
  1706. X            /* We already have a host in the list by this name. */
  1707. X            break;
  1708. X        }
  1709. X    }
  1710. X    
  1711. X    if (hp == NULL) {
  1712. X        /* If we didn't have the hostname in question in our host list,
  1713. X         * add a new one.
  1714. X         */
  1715. X        hp = (HostPtr) CALLOC1(sizeof(Host));
  1716. X        if (hp == NULL) goto memerr;
  1717. X        hp->hostname = (char *) malloc(strlen(hostname) + 1);
  1718. X        if (hp->hostname == NULL) goto memerr;
  1719. X        (void) strcpy(hp->hostname, hostname);
  1720. X
  1721. X        /* Attach hp to the host list. */
  1722. X        if (g->firstHost == NULL)
  1723. X            g->firstHost = g->lastHost = hp;
  1724. X        else {
  1725. X            g->lastHost->next = hp;
  1726. X            g->lastHost = hp;
  1727. X        }
  1728. X
  1729. X        hp->nUsers = 0;
  1730. X        hp->firstUser = hp->lastUser = NULL;
  1731. X        g->nHosts++;
  1732. X    }
  1733. X
  1734. X    if (isLocal)
  1735. X        hp->pollproc = (pollproc_t) Utmp;
  1736. X    else {
  1737. X        g->nRemoteHosts++;
  1738. X#if RPC
  1739. X        /* Try RPC first, unless you said not to. */
  1740. X        hp->pollproc = (pollproc_t) (F ? Finger : RUsers);
  1741. X#else
  1742. X        hp->pollproc = (pollproc_t) Finger;
  1743. X#endif
  1744. X    }
  1745. X    
  1746. X    up = (UserPtr) CALLOC1(sizeof(User));
  1747. X    if (up == NULL) goto memerr;
  1748. X    up->next = NULL;
  1749. X    (void) strncpy(up->username, userandhost, sizeof(up->username) - 1);
  1750. X    up->cmd = NULL;
  1751. X    if (cmd != NULL) {
  1752. X        up->cmd = (char *) malloc(strlen(cmd) + 1);
  1753. X        if (up->cmd == NULL) goto memerr;
  1754. X        (void) strcpy(up->cmd, cmd);
  1755. X    }
  1756. X    up->pollUntilActive = noIdle;
  1757. X    up->detectLogoffs = mon;
  1758. X    
  1759. X    if (hp->lastUser == NULL)
  1760. X        hp->firstUser = hp->lastUser = up;
  1761. X    else {
  1762. X        hp->lastUser->next = up;
  1763. X        hp->lastUser = up;
  1764. X    }
  1765. X    hp->nUsers++;
  1766. X
  1767. X    return (0);
  1768. X    
  1769. Xmemerr:
  1770. X    return (-1);
  1771. X}    /* AddUser */
  1772. X
  1773. X
  1774. X
  1775. X
  1776. X#if ansi
  1777. Xvoid Poll(IsonParams *g)
  1778. X#else
  1779. Xvoid Poll(g)
  1780. X    IsonParams *g;
  1781. X#endif
  1782. X{
  1783. X    HostPtr hp;
  1784. X    UserPtr up;
  1785. X    char uabuf[128], tmstr[40];
  1786. X
  1787. X    do {
  1788. X        Delay(g); /* Delay a little so we won't hog the CPU */
  1789. X        MakeSureIAmLoggedIn(g);
  1790. X
  1791. X#if DEBUG
  1792. X        if (g->debug) {
  1793. X            PrintF(g, NO_HEADER_MSG,
  1794. X                "\r\nIsOn's PID: %d;  Parent: %d.\n", g->pid, g->parentPid);
  1795. X
  1796. X            for (hp=g->firstHost; hp != NULL; hp = hp->next) {
  1797. X                PrintF(g, NO_HEADER_MSG, "\r\nHost: %-40s  Mode: %c\n",
  1798. X                    hp->hostname,
  1799. X                    hp->pollproc == (pollproc_t) Finger ? 'F'
  1800. X                    : (hp->pollproc == (pollproc_t) Utmp ? 'U' : 'R')
  1801. X                );        
  1802. X                for (up = hp->firstUser; up != NULL; up = up->next) {
  1803. X                    PrintF(g, NO_HEADER_MSG,
  1804. X                        "\r    %-8s  %c  %-3s  logons=%-2d  idle=%-2d",
  1805. X                        up->username,
  1806. X                        up->pollUntilActive ? 'I' : ' ',
  1807. X                        up->isOn ? "ON" : "off",
  1808. X                        up->logons,
  1809. X                        up->idletime
  1810. X                    );
  1811. X                    if (up->cmd != NULL)
  1812. X                        PrintF(g, NO_HEADER_MSG, "  cmd='%s'", up->cmd);
  1813. X                    PrintF(g, NO_HEADER_MSG, "\n");
  1814. X                }
  1815. X            }
  1816. X        }
  1817. X#endif    /* DEBUG */
  1818. X
  1819. X        for (hp=g->firstHost; hp != NULL; hp = hp->next) {
  1820. X            if (hp->nUsers > hp->usersDone) {
  1821. X                for (up = hp->firstUser; up != NULL; up = up->next)
  1822. X                    up->isOn = 0;
  1823. X                (*hp->pollproc)(g, hp);
  1824. X
  1825. X                /* Now go through and see if any users were logged on
  1826. X                 * the last time, but were no longer detected on.
  1827. X                 * If not, we can print a msg saying that this user
  1828. X                 * logged out.
  1829. X                 *
  1830. X                 * Note that we always do this, no matter what
  1831. X                 * up->detectLogoffs is set to, because we want to
  1832. X                 * report the rare instance where a user has been
  1833. X                 * idle, but sneaks on and logs off during ison's
  1834. X                 * sleep period.
  1835. X                 *
  1836. X                 * Basically we print the message if the user was
  1837. X                 * already on but idle then logs off before we
  1838. X                 * noticed that she wasn't idle, OR you said to
  1839. X                 * report logoffs.
  1840. X                 */
  1841. X                for (up = hp->firstUser; up != NULL; up = up->next) {
  1842. X                    if ((up->wasOn || up->wasIdle) && (!up->isOn) &&
  1843. X                        (up->detectLogoffs || up->logons == 0)) {
  1844. X                        PrintF(g, 0,
  1845. X                            "Detected log off of %s at %s.\n",
  1846. X                            UserAddress(uabuf, hp, up),
  1847. X                            TimeStr(tmstr, sizeof(tmstr), (time_t)0)
  1848. X                        );
  1849. X                        up->wasOn = up->wasIdle = 0;
  1850. X                        up->idletime = 0;
  1851. X                    }
  1852. X                }
  1853. X            }
  1854. X        }
  1855. X    } while (g->nHosts > g->hostsDone);
  1856. X
  1857. X    if (g->utmpfp != NULL)
  1858. X        (void) fclose(g->utmpfp);
  1859. X    PrintF(g, NO_BEEP_MSG, "Done!\n");
  1860. X}    /* Poll */
  1861. X
  1862. X
  1863. X
  1864. X
  1865. X#if ansi
  1866. Xvoid ReadDataFile(IsonParams *g, char *fname)
  1867. X#else
  1868. Xvoid ReadDataFile(g, fname)
  1869. X    IsonParams *g;
  1870. X    char *fname;
  1871. X#endif
  1872. X{
  1873. X    FILE *fp;
  1874. X    int usersAdded, linenum;
  1875. X    int fingerOnly, pollUntilActive, detectLogoffs;
  1876. X    char buf[256], username[64], *cp, *dp, *cmd;
  1877. X
  1878. X    if ((fp = fopen(fname, "r")) == NULL) {
  1879. X        perror(fname);
  1880. X        exit(EXIT_FATAL_ERROR);
  1881. X    }
  1882. X
  1883. X    usersAdded = linenum = 0;
  1884. X    while (fgets(buf, sizeof(buf) - 1, fp) != NULL) {
  1885. X        ++linenum;
  1886. X        for (cp = buf; ; cp++) {            /* Skip whitespace. */
  1887. X            if (*cp == '\0') goto skip;        /* Blank line? */
  1888. X            if (!isspace(*cp)) break;
  1889. X        }
  1890. X        if (*cp == COMMENT_CHAR) goto skip;
  1891. X        for (dp = cp; ; dp++) {
  1892. X            if (*dp == '\0') goto syntax;
  1893. X            if (isspace(*dp)) break;
  1894. X        }
  1895. X        *dp++ = 0;
  1896. X        (void) strcpy(username, cp);
  1897. X
  1898. X        for (; ; dp++) {                    /* Skip whitespace. */
  1899. X            if (*dp == '\0') goto addUsr;    /* No flags or cmd. */
  1900. X            if (!isspace(*dp)) break;
  1901. X        }
  1902. X
  1903. X        fingerOnly = pollUntilActive = detectLogoffs = 0;
  1904. X        cmd = NULL;
  1905. X
  1906. X        if (*dp    == '-') {
  1907. X            /* Collect options for this user. */
  1908. X            for (++dp; ; dp++) {
  1909. X                if (*dp == '\0') goto addUsr;    /* no EOLN? */
  1910. X                if (isspace(*dp)) break;
  1911. X                
  1912. X                /* Check options. */
  1913. X                if (*dp == 'I')
  1914. X                    pollUntilActive = 1;
  1915. X                else if (*dp == 'F')
  1916. X                    fingerOnly = 1;
  1917. X                else if (*dp == 'm')
  1918. X                    detectLogoffs = 1;
  1919. X                else {
  1920. X                    PrintF(g, 0, "Illegal option '%c' on line %d.\n",
  1921. X                        (int) *dp, linenum);
  1922. X                    goto skip;
  1923. X                }
  1924. X            }
  1925. X        }
  1926. X
  1927. X        for (; ; dp++) {                    /* Skip whitespace. */
  1928. X            if (*dp == '\0') goto addUsr;    /* No command given. */
  1929. X            if (!isspace(*dp)) break;
  1930. X        }
  1931. X        
  1932. X        /* The command is what's left, if any. */
  1933. X        cmd = dp;
  1934. X        
  1935. X        /* But let's strip off the EOLN. */
  1936. X        cp = dp + strlen(dp) - 1;
  1937. X        if (isspace(*cp))
  1938. X            *cp-- = '\0';
  1939. X        if (isspace(*cp))
  1940. X            *cp = '\0';
  1941. X
  1942. XaddUsr:    
  1943. X        if (AddUser(g, username, fingerOnly, pollUntilActive,
  1944. X            detectLogoffs, cmd) < 0) {
  1945. X            PrintF(g, 0, "Too many users, out of memory!\n");
  1946. X            break;
  1947. X        }
  1948. X        ++usersAdded;
  1949. X        continue;
  1950. Xsyntax:
  1951. X        PrintF(g, 0, "Syntax error on line %d.\n", linenum);
  1952. Xskip:
  1953. X        continue;
  1954. X    }
  1955. X    if (usersAdded == 0) {
  1956. X        PrintF(g, 0, "No users in data file.\n");
  1957. X        exit(EXIT_USAGE);
  1958. X    }
  1959. X}    /* ReadDataFile */
  1960. X
  1961. X
  1962. X
  1963. X
  1964. X
  1965. X#if ansi
  1966. Xvoid Usage(IsonParams *g)
  1967. X#else
  1968. Xvoid Usage(g)
  1969. X    IsonParams *g;
  1970. X#endif
  1971. X{
  1972. X    (void) fprintf(stderr, "\n\
  1973. XIsOn Usage:\n\
  1974. X    %s [-bq%s%sILmP] [-p N] [-i N] [-o outfile] [-c cmd] [-f userfile] [users]%s\n\
  1975. XUsernames:\n\
  1976. X    They can be in the form \"user@remote.host\" to poll remote hosts, or\n\
  1977. X    just \"user\" for the local host.  You can also use the -f option to\n\
  1978. X    supply a data file with many users (see the manual for the format).\n",
  1979. X                   g->progname,
  1980. X#if RPC
  1981. X                    "F",
  1982. X#else
  1983. X                    "",
  1984. X#endif
  1985. X#if DEBUG
  1986. X                    "d",
  1987. X#else
  1988. X                    "",
  1989. X#endif
  1990. X#if DAEMON
  1991. X                   ""
  1992. X#else
  1993. X                   " &"
  1994. X#endif
  1995. X    );
  1996. X
  1997. X    (void) fprintf(stderr, "\
  1998. XFlags:\n\
  1999. X   -I     : Poll until users are both logged on and not idle.\n\
  2000. X   -L     : Live (don't exit) when you log off.  Usually used with -c.\n%s%s\
  2001. X   -q     : Don't print any output at all.\n\
  2002. X   -P     : Don't print 'Polling...' message.\n\
  2003. X   -b     : %seep when IsOn prints an important message.\n\
  2004. X   -j     : %sly.\n",
  2005. X#if RPC
  2006. X                "   -F     : Use finger right away, don't bother with RPC.\n",
  2007. X#else
  2008. X                "",
  2009. X#endif
  2010. X#if DEBUG
  2011. X                "   -d     : Print debugging information while running.\n",
  2012. X#else
  2013. X                "",
  2014. X#endif
  2015. X#if BEEP
  2016. X                "Don't b",
  2017. X#else
  2018. X                "B",
  2019. X#endif
  2020. X#if DAEMON
  2021. X                "Don't go into the background automatical"
  2022. X#else
  2023. X                "Go into the background immediate"
  2024. X#endif
  2025. X    );
  2026. X
  2027. X    (void) fprintf(stderr, "\
  2028. X   -m     : Report logons and logoffs of specified users forever.\n\
  2029. X   -p N   : Seconds between iterations (defaults: local=%d, remote=%d).\n\
  2030. X   -i N   : Give up after 'N' iterations (default is ",
  2031. X                   DEFAULT_LOCAL_SLEEP,
  2032. X                   DEFAULT_REMOTE_SLEEP
  2033. X    );
  2034. X#if (MAXITER == NO_LIMIT)
  2035. X    (void) fprintf(stderr, "infinity");
  2036. X#else
  2037. X    (void) fprintf(stderr, "%ld", MAXITER);
  2038. X#endif
  2039. X
  2040. X    (void) fprintf(stderr, ").\n\
  2041. X   -o fil : Send output to 'fil' instead of the screen.\n\
  2042. X   -c cmd : Command to run for each user found (e.g. \"write %%u %%t <msg\").\
  2043. X\n%s by Phil Dietz & Mike Gleason, NCEMRSoft.\n",
  2044. X                   VERSION_STR);
  2045. X
  2046. X    exit(EXIT_USAGE);
  2047. X}                                       /* Usage */
  2048. X
  2049. X
  2050. X
  2051. X#if ansi
  2052. Xvoid main(int argc, char **argv)
  2053. X#else
  2054. Xmain(argc, argv)
  2055. X     int argc;
  2056. X     char **argv;
  2057. X#endif
  2058. X{
  2059. X    IsonParams g;
  2060. X    int flag;
  2061. X    char *cp, promptedName[64];
  2062. X    char validOpts[32];
  2063. X    char *cmd = COMMAND;
  2064. X    int fingerOnly = 0;
  2065. X    int pollUntilActive = 0;
  2066. X    int dataFileUsed = 0;
  2067. X    int detectLogoffs = 0;
  2068. X    int i;
  2069. X
  2070. X    g.sleep = -1;
  2071. X    g.progname = argv[0];
  2072. X    if ((cp = RINDEX(g.progname, '/')) != NULL)
  2073. X        g.progname = cp + 1;
  2074. X    g.daemon = DAEMON;
  2075. X    g.debug = 0;
  2076. X    g.nRemoteHosts = 0;
  2077. X    g.memInUse = 0;
  2078. X    g.autodie = 1;
  2079. X    g.maxIters = MAXITER;
  2080. X    g.iter = 0;
  2081. X    g.pollmsg = 1;
  2082. X    g.utmpfp = NULL;
  2083. X    g.parentPid = getppid();
  2084. X    g.stdinatty = isatty(0);
  2085. X    g.nHosts = g.nRemoteHosts = g.hostsDone = 0;
  2086. X    g.firstHost = g.lastHost = NULL;
  2087. X    g.canBeep = BEEP;
  2088. X    g.outfile = stderr;
  2089. X
  2090. X    (void) strcpy(validOpts, "bIjqPmLc:o:p:f:i:");
  2091. X#if RPC
  2092. X    (void) strcat(validOpts, "F");
  2093. X#endif
  2094. X#if DEBUG
  2095. X    (void) strcat(validOpts, "d");
  2096. X#endif
  2097. X
  2098. X    while ((flag = getopt(argc, argv, validOpts)) != EOF) {
  2099. X        switch (flag) {
  2100. X            case 'b':
  2101. X                g.canBeep = !g.canBeep;
  2102. X                break;
  2103. X            case 'f':
  2104. X                ReadDataFile(&g, optarg);
  2105. X                dataFileUsed = 1;
  2106. X                break;
  2107. X            case 'F':
  2108. X                fingerOnly++;
  2109. X                break;
  2110. X            case 'd':
  2111. X                g.debug++;
  2112. X                break;
  2113. X            case 'j':
  2114. X                g.daemon = !g.daemon;
  2115. X                break;
  2116. X            case 'm':
  2117. X                detectLogoffs = 1;
  2118. X                break;
  2119. X            case 'o':
  2120. X                if ((g.outfile = fopen(optarg, "a")) == NULL) {
  2121. X                    perror(optarg);
  2122. X                    exit(EXIT_FATAL_ERROR);
  2123. X                }
  2124. X                break;
  2125. X            case 'q':
  2126. X                g.outfile = NULL;
  2127. X                break;
  2128. X            case 'L':
  2129. X                g.autodie = 0;
  2130. X                break;
  2131. X            case 'c':
  2132. X                cmd = optarg;
  2133. X                break;
  2134. X            case 'P':
  2135. X                g.pollmsg = !g.pollmsg;
  2136. X                break;
  2137. X            case 'p':
  2138. X                g.sleep = atoi(optarg);
  2139. X                break;
  2140. X            case 'i':
  2141. X                g.maxIters = atol(optarg);
  2142. X                break;
  2143. X            case 'I':
  2144. X                pollUntilActive = 1;
  2145. X                break;
  2146. X            default:
  2147. X                Usage(&g);
  2148. X        }
  2149. X    }
  2150. X
  2151. X    if ((argv[optind] == NULL) && (dataFileUsed == 0)) {
  2152. X        /* No users supplied on the command line. */
  2153. X        if (g.stdinatty) {
  2154. X            (void) fprintf(stderr, "User to poll: ");
  2155. X            (void) fgets(promptedName, sizeof(promptedName), stdin);
  2156. X            promptedName[strlen(promptedName) - 1] = '\0';
  2157. X            if (promptedName[0] == '\0')
  2158. X                Usage(&g);    /* They just hit return. */
  2159. X            (void) AddUser(&g, promptedName, fingerOnly, pollUntilActive,
  2160. X                detectLogoffs, cmd);
  2161. X        } else
  2162. X            Usage(&g);    /* Can't prompt from a shell script, etc. */
  2163. X    } else {
  2164. X        for (i=optind; i<argc; i++) {
  2165. X            if (AddUser(&g, argv[i], fingerOnly, pollUntilActive,
  2166. X                detectLogoffs, cmd) < 0) {
  2167. X                PrintF(&g, 0, "Too many users, out of memory!\n");
  2168. X                break;
  2169. X            }
  2170. X        }
  2171. X    }
  2172. X
  2173. X
  2174. X    /* Print a warning if the user chose a scenario where we know we
  2175. X     * will never exit!
  2176. X     */
  2177. X    if (detectLogoffs && !g.autodie) {
  2178. X        PrintF(&g, 0,
  2179. X"NOTE: since you enabled continuous log on/off detection and turned off\n\
  2180. Xself-termination when you logoff, ison will run forever!  You will have to\n\
  2181. Xuse 'ps -u yourname' or some such to kill it later.");
  2182. X    }
  2183. X
  2184. X    /* Don't leave ^G's in actual files. */
  2185. X    if (g.outfile != stderr)
  2186. X        g.canBeep = 0;
  2187. X
  2188. X#if NICE
  2189. X    /* lower our process' priority (nice) */
  2190. X    (void) nice(20);
  2191. X#endif
  2192. X
  2193. X    if (g.daemon) {
  2194. X        if (fork())                       /* automatically puts this task in
  2195. X                                        * background! */
  2196. X            exit(EXIT_PARENT);
  2197. X
  2198. X        (void) signal(SIGINT, SIG_IGN);
  2199. X        (void) signal(SIGQUIT, SIG_IGN);
  2200. X    }
  2201. X
  2202. X    (void) sleep(1);    /* wait for your shell prompt to catch up. */
  2203. X    (void) signal(SIGHUP, SIG_DFL);
  2204. X
  2205. X    g.pid = getpid();
  2206. X
  2207. X    Poll(&g);    /* Finally, get down to business. */
  2208. X
  2209. X    exit(EXIT_SUCCESSFUL);
  2210. X}                                       /* main */
  2211. X
  2212. X/* eof ison.c */
  2213. END_OF_FILE
  2214. if test 30133 -ne `wc -c <'ison.c'`; then
  2215.     echo shar: \"'ison.c'\" unpacked with wrong size!
  2216. fi
  2217. # end of 'ison.c'
  2218. fi
  2219. echo shar: End of shell archive.
  2220. exit 0
  2221. --
  2222. --mg                                                      mgleason@cse.unl.edu
  2223.  
  2224. exit 0 # Just in case...
  2225.