/doc/todo/fileupload/

l='vcs-git' href='user@source.jones.dk:bin' title='bin Git repository'/>
summaryrefslogtreecommitdiff
path: root/machine-update-beta
blob: 8c1770ff9ade2ddca04e81195c025e838838df9d (plain)
  1. #!/usr/bin/perl -w
  2. # For Emacs: -*- mode:cperl; mode:folding; -*-
  3. #
  4. # Get a machine's critical features, And mail/http them to the Linux Counter
  5. #
  6. # (c) 1999 - Harald Tveit Alvestrand, the Linux Counter Project
  7. # 2003 - PetaMem Group (www.petamem.com)
  8. # License: GNU Copyleft - see bottom of file.
  9. # Changelog: see even more bottom of the file
  10. #
  11. # As a matter of courtesy, if you change this file on your own,
  12. # make sure it does NOT mail to the counter!
  13. #
  14. use strict;
  15. use POSIX;
  16. our $VERSION = '0.25';
  17. our $CVS_VERSION = '$Revision: 1.2 $ $Date: 2006-02-06 18:54:04 $ $Author: jonas $';
  18. our $IsInTestHarness;
  19. use vars qw(%values %oldvalues $errordata $debugdata %files); # data that is sent
  20. use vars qw($progname %option);
  21. use vars qw(%is_sys_account %is_user %is_account);
  22. # stuff that controls defaults for passwdscan & accounts subroutines
  23. my ($UID_MIN, $UID_MAX, $got_defs) = (100, 65533, '');
  24. # Make sure nothing happens, so that the script's routines
  25. # can be debugged from another file
  26. return 1 if($IsInTestHarness);
  27. &preparation;
  28. &options;
  29. &readfile;
  30. &checkconfig;
  31. if ($option{ask}) {
  32. &askquestions;
  33. }
  34. &writefile;
  35. &sendfile;
  36. # {{{ preparation
  37. #
  38. sub preparation {
  39. die "No HOME environment variable\n" if (!$ENV{HOME});
  40. die "No home diretory\n" if ! -d $ENV{HOME};
  41. # Kill some internationalization
  42. $ENV{LANG} = 'C';
  43. delete $ENV{LC_CTYPE};
  44. delete $ENV{LC_NUMERIC};
  45. delete $ENV{LC_NAME};
  46. delete $ENV{LC_TIME};
  47. delete $ENV{LC_MESSAGES};
  48. delete $ENV{LC_COLLATE};
  49. delete $ENV{LC_MONETARY};
  50. my $infodir = "$ENV{HOME}/.linuxcounter";
  51. if (! -d $infodir) {
  52. mkdir($infodir, 0766) || die "Unable to make $infodir\n";
  53. }
  54. # Keep track of where I am; need it to install crontab entry
  55. # progname is a global.
  56. $progname = $0;
  57. if ($progname !~ /^\//) {
  58. my $progdir = `pwd`;
  59. chop $progdir;
  60. $progname = "$progdir/$progname";
  61. $progname =~ s!/./!/!;
  62. }
  63. chdir($infodir) || die "Unable to change to $infodir\n";
  64. my ($sysname, $nodename, $release, $version, $machine ) = POSIX::uname();
  65. if (! -f $nodename) {
  66. print STDERR "Machine-update $VERSION. Use $0 -l to display license.\n";
  67. print STDERR "Creating the infofile for your computer.\n";
  68. # Create the infodir
  69. open(INFO, ">$nodename");
  70. print INFO "uniqueid: ", randomnumber(), "\n";
  71. close INFO;
  72. }
  73. srand time % $$; # do some seed "randomization"
  74. }
  75. # }}}
  76. # {{{ options
  77. #
  78. sub options {
  79. my $opt;
  80. while (defined($ARGV[0]) && $ARGV[0] =~ /^-/) {
  81. $opt = shift @ARGV;
  82. $opt =~ /c/ && &installcrontab;
  83. $opt =~ /d/ && $option{DEBUG}++ && print STDERR "Debug is $option{DEBUG}\n";
  84. $opt =~ /h/ && &help;
  85. $opt =~ /i/ && ($option{ask} = 1);
  86. $opt =~ /l/ && &license;
  87. $opt =~ /m/ && ($option{mail} = 1);
  88. $opt =~ /t/ && ($option{mail} = 0);
  89. $opt =~ /u/ && &uninstallcrontab;
  90. $opt =~ /v/ && die "\n\t Linux Counter machine-update version $VERSION\n"
  91. . "\tCVS version $CVS_VERSION\n";
  92. $opt =~ /x/ && ($option{info} = 1);
  93. }
  94. }
  95. # }}}
  96. # {{{ askquestions
  97. #
  98. sub askquestions {
  99. return if ! -t STDIN || ! -t STDOUT;
  100. $| = 1;
  101. print "Here you can specify some info that the script can't know for itself\n";
  102. $values{owner} = askone("Your Linux Counter reg#, if any", $values{owner});
  103. $values{key} = askone("Your machine's counter reg#, if any", $values{key});
  104. }
  105. # }}}
  106. # {{{ askone
  107. #
  108. sub askone {
  109. my $prompt = shift;
  110. my $default = shift;
  111. print $prompt;
  112. if (defined($default)) {
  113. print " [$default]";
  114. }
  115. print ':';
  116. my $ans = <STDIN>;
  117. chop $ans;
  118. &Debug("Answer was $ans\n");
  119. $ans = $default if (!length($ans));
  120. return $ans;
  121. }
  122. # }}}
  123. # {{{ readfile
  124. #
  125. sub readfile {
  126. my ($sysname, $nodename, $release, $version, $machine ) = POSIX::uname();
  127. open(INFO, $nodename) || die "Did not find infofile $nodename\n";
  128. while (<INFO>) {
  129. chop;
  130. s/#.*//;
  131. if (/^(\S+): *(.+)/) {
  132. my $key = $1;
  133. my $value = $2;
  134. if ($1 !~ /^(owner|key|uniqueid)$/) {
  135. next;
  136. }
  137. &Debug("Read $key: $value\n");
  138. $values{$key} = $value;
  139. } else {
  140. print STDERR "Unparsed info line: $_ - discarded\n";
  141. }
  142. }
  143. close INFO;
  144. %oldvalues = %values;
  145. }
  146. # }}}
  147. # {{{ writefile
  148. #
  149. sub writefile {
  150. my ($sysname, $nodename, $release, $version, $machine ) = POSIX::uname();
  151. open(INFO, ">$nodename.new");
  152. for my $val (sort keys(%values)) {
  153. &Debug("Saving $val: $values{$val}\n");
  154. print INFO "$val: $values{$val}\n";
  155. }
  156. close INFO;
  157. rename("$nodename.new", $nodename) || die "Rename failed\n";
  158. }
  159. # }}}
  160. # {{{ sendfile
  161. #
  162. sub sendfile {
  163. if ($option{mail}) {
  164. open(MAIL, "|/usr/lib/sendmail machine-registration\@counter.li.org")
  165. || die "Unable to open sendmail\n";
  166. } else {
  167. warn "--------------------------------------------------------\n";
  168. warn "This is what will be sent to the Linux Counter if you\n";
  169. warn "run the program with the -m switch. Now, NOTHING IS SENT\n";
  170. warn "--------------------------------------------------------\n";
  171. open(MAIL, ">&STDOUT");
  172. }
  173. # note that $ENV{USER} isn't (always) set in a cron job...
  174. my $user = (getpwuid($<))[0];
  175. $user = "unknown-id-$<" if !$user;
  176. print MAIL <<EOF
  177. From: $user
  178. To: machine-registration\@counter.li.org
  179. Subject: machine-update for $values{name}
  180. //MACHINE
  181. EOF
  182. ;
  183. for my $val (sort keys(%values)) {
  184. print MAIL "$val: $values{$val}\n"
  185. if length($values{$val}) > 0;
  186. }
  187. print MAIL "//END\n";
  188. # Attach files
  189. for my $file (keys(%files)) {
  190. print MAIL "//FILE $file\n";
  191. print MAIL $files{$file};
  192. print MAIL "//EOF\n";
  193. }
  194. # Attach possible other info
  195. if ($errordata) {
  196. print MAIL "----- Problem info gathered during probing -----\n";
  197. print MAIL $errordata;
  198. }
  199. $option{info} && do {
  200. print MAIL "----- Debug data for the script maintainer's aid -----\n";
  201. print MAIL $debugdata;
  202. };
  203. close MAIL;
  204. }
  205. # }}}
  206. # {{{ randomnumber
  207. #
  208. sub randomnumber {
  209. return int(rand(1_000_000_000));
  210. }
  211. # }}}
  212. # {{{ checkconfig
  213. #
  214. sub checkconfig {
  215. my ($sysname, $nodename, $release, $version, $machine ) = POSIX::uname();
  216. warn "This is not Linux, but $sysname!\n" if($sysname ne 'Linux');
  217. $values{method} = "machine-update version $VERSION";
  218. $values{os} = $sysname;
  219. $values{kernel} = $release;
  220. $values{cpu_uname} = $machine;
  221. $values{name} = $nodename; # First order guess
  222. # Credit for some of the code below goes to
  223. # Denis Havlik: <havlik@ap.univie.ac.at>
  224. # Blame is, of course, all mine - HTA -
  225. # Note - there are numerous problems with df, including:
  226. # - early versions don't support the -l option
  227. # - at least some include SAMBA filesystems in the -l option
  228. # 1: Snarf a df -T
  229. my $dfbin = &xbin("df");
  230. $files{"df -T"} = `$dfbin -T -x nfs`;
  231. $values{accounts} = &accounts;
  232. $values{users} = &active_users;
  233. my $uptime = &xbin('uptime');
  234. if($uptime) {
  235. $uptime = `$uptime`;
  236. $values{uptime_1} = $uptime; # preserve raw version
  237. $values{uptime_1} =~ s/\n.*//;
  238. }
  239. my $lastprog = xbin('last');
  240. if ($lastprog && -r "/var/run/utmp") {
  241. $values{uptime_2} = `$lastprog -xf /var/run/utmp runlevel`;
  242. $values{uptime_2} =~ s/\n.*$//s;
  243. } else {
  244. DebugInfo("Can't do last to find uptime");
  245. }
  246. # Not sure this is a Right Thing...so not saving it for the moment
  247. # This section based on a patch from Mark-Jason Dominus <mjd@plover.com>
  248. # try to guess mailer based on content of /usr/lib/sendmail link
  249. if (-l '/usr/lib/sendmail') {
  250. my $realsendmail = readlink('/usr/lib/sendmail');
  251. if ($realsendmail eq '../sbin/sendmail') {
  252. $realsendmail = '/usr/sbin/sendmail';
  253. if (-l $realsendmail) {
  254. $realsendmail = readlink($realsendmail);
  255. }
  256. }
  257. if ($realsendmail =~ m{^/var/qmail}) {
  258. $values{mailer} = "qmail";
  259. } else {
  260. &DebugInfo("Found sendmail as a link to $realsendmail\n");
  261. }
  262. }
  263. # Link method did not work. Try to guess based on presence of
  264. # config files. (this is more susceptible to the old-junk problem)
  265. if (!$values{mailer}) {
  266. if ( -d '/var/qmail') {
  267. $values{mailer} = 'qmail';
  268. } elsif ( -f '/etc/sendmail.cf' || -f '/etc/mail/sendmail.cf') {
  269. # TMDG claims recent Fedora Core has it in /etc/mail/sendmail.cf
  270. $values{mailer} = 'sendmail';
  271. } elsif ( -d '/etc/postfix') {
  272. $values{mailer} = 'postfix';
  273. }
  274. }
  275. $values{kcoresize} = -s "/proc/kcore";
  276. addonefileforsending("/proc/meminfo");
  277. addonefileforsending("/proc/cpuinfo");
  278. addonefileforsending("/proc/version");
  279. # info on what devices are in use on the system
  280. addonefileforsending("/proc/pci");
  281. addonefileforsending("/proc/bus/usb/devices");
  282. # Both Mandrake and Red Hat use this file....
  283. addonefileforsending("/etc/redhat-release");
  284. }
  285. # }}}
  286. # {{{ accounts
  287. #
  288. sub accounts {
  289. my $s;
  290. my $niss;
  291. my $ypcatbin; # will hold path to the ypcat binary (if any)
  292. open (TMP,"</etc/passwd");
  293. $s += &passwdscan;
  294. &DebugErr("Found $s accounts total\n");
  295. &Debug("Switching to NIS passwords\n");
  296. $ypcatbin = &xbin('ypcat'); # get path to ypcat binary (empty if none)
  297. if($ypcatbin) { # test whether ypcat was found
  298. open TMP, "$ypcatbin passwd 2> /dev/null|"
  299. || ($errordata .= "ypcat failed: $!\n");
  300. $niss = &passwdscan;
  301. $s += $niss;
  302. close TMP;
  303. &Debug("Status of ypcat: $?\n");
  304. &DebugErr("Found $niss accounts in ypcat passwd\n");
  305. }
  306. &DebugErr('Sysaccounts: ', join(' ', keys(%is_sys_account)), "\n");
  307. &DebugErr("Found $s accounts total\n");
  308. return $s;
  309. }
  310. # }}}
  311. # {{{ passwdscan
  312. #
  313. sub passwdscan {
  314. # Code for reading login.defs courtesy of Vassilii Khachaturov
  315. # <vassilii@tarunz.org>
  316. local (*DEFS);
  317. # Try importing UID_MIN and UID_MAX from /etc/login.defs, if possible
  318. # else just assume the above defaults for min and max non-system UID
  319. if (!$got_defs && open (DEFS, '/etc/login.defs')) {
  320. while (<DEFS>) {
  321. if (/^\s*(UID_(?:MIN|MAX))\s+(\d+)/) {
  322. # elegant, but not compatible with "strict refs":
  323. #${ $1 } = $2;
  324. if ($1 eq "UID_MIN") {
  325. $UID_MIN = $2;
  326. } else {
  327. $UID_MAX = $2;
  328. }
  329. &Debug("DEFS match: $1 = $2\n");
  330. }
  331. }
  332. close (DEFS);
  333. $got_defs = 1;
  334. }
  335. &Debug("UID_MIN = $UID_MIN, UID_MAX = $UID_MAX\n");
  336. # I suppose this is as good as it gets -
  337. # Usually user accounts have UID > 100 and
  338. # "system accounts" have UID < 100, but there is no guarantee
  339. # that
  340. # this will hold for pseudo-users like "postgress" etc.
  341. # Also nobody is usually 99 on linux, but -1 on "standard" unices.
  342. # RedHat places the dividing line at 500. Others use 400...
  343. my @line;
  344. my $s = 0;
  345. while (<TMP>) {
  346. @line = split ':';
  347. if ($line[2] >= $UID_MIN && $line[2] <= $UID_MAX
  348. && !($line[0] eq 'nobody')) {
  349. $s++;
  350. $is_account{$line[0]} = 1;
  351. } else {
  352. $is_sys_account{$line[0]} = 1;
  353. }
  354. }
  355. return $s;
  356. }
  357. # }}}
  358. # {{{ active_users
  359. #
  360. # This is kind of alpha, but please test it.
  361. # It calculates the number of "active" users based on the "wtmp" entries
  362. # unfortunately at least Mandrake 8 and 9 ship with non-world-read wtmp
  363. # and non-set-uid last, so this does not work any more...
  364. #
  365. # RJ: Actually I think the best thing to do is to bury this code and be silent about it.
  366. #
  367. sub active_users {
  368. my $userslisted;
  369. for (qw(reboot wtmp runlevel)) { # This sysaccounts shouldn't be counted. Who else?
  370. $is_sys_account{$_} = 1;
  371. }
  372. open( TMP, "/usr/bin/last 2>&1|");
  373. while (<TMP>) {
  374. chop;
  375. if (m!/var/log/wtmp: Permission denied!) { # RJ: ***Boom*** on every non-EN system
  376. &ErrorInfo("/usr/bin/last failed because /var/log/wtmp isn't readable\n");
  377. last;
  378. }
  379. last if(!$_); # RJ: quick hack to safe bad code from harm
  380. my @tmp = split;
  381. my $name = $tmp[0];
  382. if ($is_sys_account{$name}) {
  383. # do nothing
  384. } elsif (defined $is_account{$name}) {
  385. $is_user{$name} = 1;
  386. } elsif (/^\s*$/) { # blank line - do nothing
  387. } elsif ($#tmp == 9) { # OK line, but unknown user
  388. $option{DEBUG} && do {
  389. if (!$userslisted) {
  390. print STDERR 'Know users are: ',
  391. join(' ', keys(%is_account)), "\n";
  392. $userslisted = 1;
  393. }
  394. print STDERR "Unknown user: $name\n";
  395. }
  396. } else {
  397. &DebugErr("Strange line: $_\n");
  398. }
  399. }
  400. close TMP;
  401. my $i = 0;
  402. for (sort keys %is_user) {
  403. $option{DEBUG} && printf "Active user %3d: %s\n", ++$i, $_;
  404. }
  405. &Debug("$i active users found.\n");
  406. return $i;
  407. }
  408. # }}}
  409. # {{{ installcrontab
  410. #
  411. sub installcrontab {
  412. my $hour = int(rand(24));
  413. my $min = int(rand(60));
  414. my $day = int(rand(7)); # Weekday. This version runs once a week.
  415. my $cron = "";
  416. warn "Installing start of script into your crontab\n";
  417. if (open(CRON, "crontab -l |")) {
  418. &Debug("Checking crontab for machine-update\n");
  419. &Debug("Want to install as $progname\n");
  420. while (<CRON>) {
  421. if (/^#/ && $. <= 3) { # initial comment
  422. &Debug("Skipping comment: $_");
  423. next;
  424. }
  425. if (/machine-update/) {
  426. if (/ $progname -m/) {
  427. die "Crontab entry already installed: $_\n";
  428. } else {
  429. die "Another entry with machine-update: $_\n";
  430. }
  431. }
  432. $cron .= $_;
  433. }
  434. close CRON;
  435. &Debug("Result from crontab -l: ", $? / 256, "\n");
  436. if ($? == 0) {
  437. &Debug("Crontab successfully read\n");
  438. } elsif ($? == 256) {
  439. warn "You don't seem to have a crontab. I will create one.\n";
  440. } else {
  441. die "Failed to read your crontab. Please report this as a bug: $?\n";
  442. }
  443. } else {
  444. &Debug("Result from crontab open(): $?\n");
  445. die "Unable to execute crontab command. Please check your system\n";
  446. }
  447. open(CRON, "|crontab -");
  448. print CRON $cron;
  449. print CRON "$min $hour * * $day $progname -m\n";
  450. close CRON;
  451. &Debug("Result from crontab: $?\n");
  452. if ($?) {
  453. die(<<EoF);
  454. Installing new crontab failed.
  455. YOUR CRONTAB MAY BE DAMAGED - use crontab -l to check it.
  456. Here's its former content (if any):
  457. $cron