Advertisement
Kimarite

lhg-connector: scan_hardware (PPA)

Jul 1st, 2017
668
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.41 KB | None | 0 0
  1. #!/usr/bin/perl
  2.  
  3. # This script scans your hardware and sends the collected data
  4. # to linux-hardware-guide.com
  5. # It uses standard tools like lsusb, lscpi, lsb_release to gather data,
  6. # sends it to our database, and then creates a link to the result page.
  7. #
  8. # It is intended as a simple method to add your hardware components to
  9. # the Linux-Hardware-Guide database.
  10. #
  11. # Latest version of the script can be found at:
  12. # https://github.com/paralib5/lhg_scanner
  13.  
  14. use Getopt::Long qw(GetOptions);
  15. Getopt::Long::Configure qw(gnu_getopt);
  16.  
  17. $VERSION = "0.5.1";
  18.  
  19. # Options from command line in upper case
  20. $DEBUG = 0; # run in debug mode
  21. $SILENT = 0; # run in batch mode, no user interaction
  22. $USERID = ""; # LHG userid
  23. $USERIDDE = ""; # LHG.de userid
  24. $COMMENTID = ""; # Link with comment
  25.  
  26. $TMPDIR = "$ENV{HOME}/.tmp/LHG.".$$;
  27.  
  28. GetOptions (
  29. "silent" => \$SILENT,
  30. "debug" => \$DEBUG,
  31. "c=s" => \$COMMENTID,
  32. "d=s" => \$USERIDDE,
  33. "u=s" => \$USERID
  34. );
  35.  
  36. # The Linux-Hardware-Guide library server
  37. $upload_server="http://library.linux-hardware-guide.com";
  38.  
  39. # The Linux-Hardware-Guide configuration file
  40. $lhg_conf_filename="/etc/lhg-tools.conf";
  41.  
  42. check_configuration_file();
  43.  
  44. print "***********\n";
  45. print "* This is the hardware scan program of \n";
  46. print "* http://www.Linux-Hardware-Guide.com\n";
  47. print "* \n";
  48. print "* It will check your hardware and send the results to \n";
  49. print "* the Linux-Hardware-Guide\n";
  50. print "* \n";
  51. print "* Log outputs will be stored in: ";
  52. print "$TMPDIR \n";
  53. print "***********\n";
  54. if ($DEBUG) { print "- Debug mode enabled\n"; };
  55. if ($SILENT) { print "- Silent mode (batch) enabled\n"; };
  56. if ($USERID) { print "- User id = $USERID\n"; };
  57. if ($USERIDDE) { print "- User id (de) = $USERIDDE\n"; };
  58. if ($COMMENTID) { print "- Comment id = $COMMENTID\n"; };
  59. print "\n";
  60.  
  61. my @allfiles = ("alsa_cards.txt","alsa_devices.txt","aplay.txt","cpuinfo.txt",
  62. "dd.err.txt","dd.txt","dmesg.txt","lsb_release.txt","lspci.txt",
  63. "lsusb.txt","lsusb_v.err.txt","lsusb_v.txt","scanimage.txt",
  64. "version.txt","hdparm.txt","hdparm_direct.txt");
  65.  
  66. # Harddisk check only possible as root.
  67. # However, this test (and therefore root privileges) is not mandatory
  68. check_root();
  69.  
  70. print "Collecting data:\n";
  71.  
  72. system ("mkdir -p $TMPDIR");
  73.  
  74. # System information
  75. print " -> general system information \n";
  76. system ("lsb_release -a > $TMPDIR/lsb_release.txt");
  77. system ("cat /etc/*-release >> $TMPDIR/lsb_release.txt");
  78. system ("cat /proc/version > $TMPDIR/version.txt");
  79.  
  80. # General HW overview
  81. print " -> general hardware overview \n";
  82. system ("lsusb > $TMPDIR/lsusb.txt");
  83. system ("lsusb -v > $TMPDIR/lsusb_v.txt 2> $TMPDIR/lsusb_v.err.txt");
  84.  
  85. # lspci needed to be started with absolute path for some distribution (e.g. OpenSUSE)
  86. if (-e "/usr/bin/lspci") {
  87. system ("/usr/bin/lspci -nnk > $TMPDIR/lspci.txt");
  88. }elsif (-e "/sbin/lspci") {
  89. system ("/sbin/lspci -nnk > $TMPDIR/lspci.txt");
  90. }else{
  91. system ("lspci -nnk > $TMPDIR/lspci.txt");
  92. }
  93. system ("cat /proc/cpuinfo > $TMPDIR/cpuinfo.txt");
  94.  
  95. # system boot messages
  96. print " -> boot messages \n";
  97. system ("dmesg > $TMPDIR/dmesg.txt");
  98.  
  99. # system boot messages
  100. print " -> graphic card/drivers \n";
  101. check_graphics();
  102.  
  103. # Scanner search
  104. print " -> checking scanners \n";
  105. system ("scanimage -L > $TMPDIR/scanimage.txt");
  106.  
  107. # Sound system information
  108. print " -> checking sound system \n";
  109. system ("cat /proc/asound/devices > $TMPDIR/alsa_devices.txt");
  110. system ("cat /proc/asound/cards > $TMPDIR/alsa_cards.txt");
  111. system ("aplay -l > $TMPDIR/aplay.txt");
  112.  
  113.  
  114. # HDD measurement
  115. print " -> checking harddisk\n";
  116. check_drives();
  117.  
  118.  
  119. #The HDD write test uses a significant amount of space for its temporary file
  120. #We skip this test if there is not enough space left on the drive
  121. $available_space = `df $TMPDIR | awk 'NR==2 {print \$4}'`;
  122. if ($available_space > 1073741824/1024*1.5){ # 1.5 ... safety margin
  123. system("dd if=/dev/zero of=$TMPDIR/dd_tempfile bs=1M count=1024 conv=fdatasync,notrunc > $TMPDIR/dd.txt 2> $TMPDIR/dd.err.txt");
  124. system("rm $TMPDIR/dd_tempfile");
  125. }else{
  126. print "Skipping HDD write test due to limited storage capacity\n";
  127. }
  128.  
  129. #only possible as root
  130. if ( $< == 0 ) {
  131. system ("hdparm -tT /dev/sda > $TMPDIR/hdparm.txt");
  132. system("hdparm -tT --direct /dev/sda > $TMPDIR/hdparm_direct.txt");
  133. }else{
  134. #create empty dummy file
  135. system ("touch $TMPDIR/hdparm.txt");
  136. system ("touch $TMPDIR/hdparm_direct.txt");
  137. }
  138.  
  139. # Equipment paired with Logitech Unifying Receivers is hard to distinguish
  140. # We will need some help by other tools here
  141. print " -> checking Unifying Receivers \n";
  142. check_unifying_receivers();
  143. read_dmi();
  144.  
  145. misc_stats();
  146.  
  147. print " -> Make data anonymous\n";
  148. anonymize_data();
  149.  
  150. print "done\n";
  151.  
  152. if (! $DEBUG) {
  153. # Upload request and open browser
  154. print "\nUploading data\n";
  155. upload_files();
  156. }
  157.  
  158. exit 0;
  159.  
  160. $mailtext ="Please fill out the following mail body:
  161.  
  162. 1) Hardware to be added to the database:
  163. (Please enter the hardware name)
  164.  
  165. 2) Hardware URL:
  166. (Please add a link to the manufacturer's or Amazon's web site where details of the hardware can be found)
  167.  
  168. 3) Describe the Linux compatibility of the Hardware:
  169. (e.g. works out-of-the-box, necessary configuration steps)
  170.  
  171. 4) Please send this mail with its attachments to webmaster\@linux-hardware-guide.com
  172.  
  173. Thank you for your submission!
  174. ";
  175.  
  176. $files = create_attachment_string();
  177.  
  178. system ("thunderbird -compose \"to='webmaster\@linux-hardware-guide.com',subject='Linux Hardware - automatic scan',body='$mailtext',attachment='$files'\"");
  179.  
  180. exit 0;
  181.  
  182. sub read_configfile {
  183.  
  184. # read preferences from configuration key-value file
  185. # only the keys defined in the hash %prefs are read,
  186. # all other stuff is ignored
  187.  
  188. my $configfile = shift;
  189. my $prefs = shift;
  190.  
  191. if (! -e $configfile) {
  192. return 0;
  193. };
  194.  
  195. open(my $fh, $configfile);
  196. while (<$fh>) {
  197. chomp; # no newline
  198. s/#.*//; # no comments
  199. s/^\s+//; # no leading white
  200. s/\s+$//; # no trailing white
  201. next unless length; # anything left?
  202. my ($var, $value) = split(/\s*=\s*/, $_, 2);
  203. if (exists $$prefs{$var}) {
  204. $$prefs{$var} = $value;
  205. }
  206. }
  207. close($fh);
  208. return 1;
  209. }
  210.  
  211. sub ask_for_proxy_credentials {
  212. # first test if we have the necessary Perl module installed
  213. my $modtest = eval {
  214. require IO::Prompter;
  215. IO::Prompter->import();
  216. 1;
  217. };
  218. if (! $modtest) {
  219. print "Error: Module IO::Prompter not found!\n";
  220. return ("", "");
  221. }
  222. # second ask the user for input
  223. print "Proxy requires username and password\n";
  224. my $username = prompt( "Username: " );
  225. my $password = prompt( "Password: ", -echo=>'*');
  226. return ($username, $password);
  227. }
  228.  
  229. sub check_proxy {
  230.  
  231. # check if we access library.linux-hardware-guide.com via a proxy
  232. # returns LWP::UserAgent object
  233.  
  234. use URI::Split qw(uri_split uri_join);
  235.  
  236. my $ua = LWP::UserAgent->new;
  237.  
  238. if ($ENV{http_proxy} eq "") {
  239. print "No http proxy defined.\n";
  240. return $ua;
  241. }
  242.  
  243. $ua->env_proxy;
  244. my $response = $ua->get($upload_server);
  245. if ($response->code == 407) {
  246.  
  247. my (@parts) = uri_split($ENV{http_proxy});
  248. my $proxyurl;
  249. my %proxyprefs = (
  250. 'proxy_user' => "",
  251. 'proxy_password' => ""
  252. );
  253.  
  254. # try to fetch credentials from ~/.wgetrc
  255. my $configfile = $ENV{"HOME"}."/.wgetrc";
  256. if (!read_configfile($configfile, \%proxyprefs)) {
  257. print "Configuration file for proxy credentials not found: " . $configfile . "\n";
  258. }
  259. if ($proxyprefs{'proxy_user'} && $proxyprefs{'proxy_password'}) {
  260. # found credentials
  261. $proxyurl = uri_join(@parts[0], $proxyprefs{'proxy_user'} . ":" . $proxyprefs{'proxy_password'} . "@" . @parts[1]);
  262. }
  263. else {
  264. if ($SILENT) {
  265. # user prompt will not work in --silent mode
  266. # do not ask for proxy data, end program with error message
  267. print "Error: proxy username and password needed\n";
  268. exit 1;
  269. }
  270. else {
  271.  
  272. # username and password are required, but cannot found
  273. # so the last chance ist to query the user about it
  274. my ($u, $p) = ask_for_proxy_credentials();
  275. if (! ($u || $p)) {
  276. print "Error: Could not get proxy credentials. Terminating.\n";
  277. exit 1;
  278. }
  279. $proxyurl = uri_join(@parts[0], $u . ":" . $p . "@" . @parts[1]);
  280. }
  281. }
  282. $ua->proxy(['http', 'ftp'], $proxyurl);
  283. }
  284. elsif ($response->code != 200) {
  285. print "Warning: Unexpected response code ". $response->code . "\n";
  286. }
  287.  
  288. return $ua;
  289. };
  290.  
  291. sub upload_files {
  292.  
  293. my $sid;
  294. use LWP::UserAgent;
  295. use HTTP::Request::Common;
  296.  
  297. # get session ID
  298. $request = HTTP::Request->new(GET => $upload_server."/uploadrequest.php?v=0.2");
  299. $ua = check_proxy;
  300. $response = $ua->request($request);
  301. if ($response->code == 200) {
  302. # HTTP_OK
  303. $sid = $response->content;
  304. } else {
  305. print "Error: obtaining session id for upload failed!";
  306. return 0;
  307. }
  308.  
  309. # upload files
  310. $response = $ua->request(POST $upload_server."/uploadfiles.php",
  311.  
  312. Content_Type => 'form-data',
  313.  
  314. Content => [ sid => $sid,
  315. file1 => ["$TMPDIR/".$allfiles[0]],
  316. file2 => ["$TMPDIR/".$allfiles[1]],
  317. file3 => ["$TMPDIR/".$allfiles[2]],
  318. file4 => ["$TMPDIR/".$allfiles[3]],
  319. file5 => ["$TMPDIR/".$allfiles[4]],
  320. file6 => ["$TMPDIR/".$allfiles[5]],
  321. file7 => ["$TMPDIR/".$allfiles[6]],
  322. file8 => ["$TMPDIR/".$allfiles[7]],
  323. file9 => ["$TMPDIR/".$allfiles[8]],
  324. file10 => ["$TMPDIR/".$allfiles[9]],
  325. file11 => ["$TMPDIR/".$allfiles[10]],
  326. file12 => ["$TMPDIR/".$allfiles[11]],
  327. file13 => ["$TMPDIR/".$allfiles[12]],
  328. file14 => ["$TMPDIR/".$allfiles[13]],
  329. file15 => ["$TMPDIR/".$allfiles[14]],
  330. file16 => ["$TMPDIR/".$allfiles[15]],
  331. ]
  332.  
  333. );
  334.  
  335. print " -> ";
  336. print "(".$response->code.") :";
  337. print $response->content;
  338.  
  339. if ($response->code == HTTP::Status::HTTP_REQUEST_TIMEOUT) {
  340. print "Connection timeout. Upload failed \n";
  341. exit 3;
  342. }
  343. elsif ($response->code == 500 ) {
  344. print "Bad Hostname, upload failed. Please check your internet connection. \n";
  345. exit 4;
  346. }
  347.  
  348. print "\n\nOpening scan results at:\nhttp://www.linux-hardware-guide.com/hardware-profile/scan-".$sid."\n";
  349.  
  350. # store help in file
  351.  
  352. open FILE_OUT, ">", "$TMPDIR/readme.txt";
  353. print FILE_OUT "This directory contains the log messages sent to the Linux-Hardware-Guide.\n";
  354. print FILE_OUT "http://www.Linux-Hardware-Guide.com\n";
  355. print FILE_OUT "\n";
  356. print FILE_OUT "You can find your processed scan results at \n";
  357. print FILE_OUT "http://www.linux-hardware-guide.com/hardware-profile/scan-".$sid."\n";
  358. close FILE_OUT;
  359.  
  360. if ($SILENT) {
  361. #service mode -> do nothing
  362. }else{
  363. open_browser($sid);
  364. }
  365.  
  366.  
  367. print "\n\n\n****************************** ";
  368. print "\nYour scan results can be found at";
  369. print "\nhttp://www.linux-hardware-guide.com/hardware-profile/scan-".$sid."";
  370. print "\n****************************** \n\n";
  371.  
  372. exit 0;
  373.  
  374. }
  375.  
  376. sub open_browser {
  377. my $sid = shift;
  378.  
  379. print "Trying to open scan results in browser...\n";
  380.  
  381. if (-e "/usr/bin/sensible-browser") {
  382. system "sensible-browser http://www.linux-hardware-guide.com/hardware-profile/scan-$sid 2>> $TMPDIR/errors.txt ";
  383. }elsif (-e "/usr/bin/xdg-open") {
  384. system "sensible-browser http://www.linux-hardware-guide.com/hardware-profile/scan-$sid 2>> $TMPDIR/errors.txt";
  385. }elsif (-e "/usr/bin/firefox") {
  386. system "firefox --new-tab http://www.linux-hardware-guide.com/hardware-profile/scan-$sid 2>> $TMPDIR/errors.txt";
  387. }elsif (-e "/usr/bin/chromium-browser") {
  388. system "chromium-browser --app=http://www.linux-hardware-guide.com/hardware-profile/scan-$sid 2>> $TMPDIR/errors.txt";
  389. }
  390.  
  391.  
  392. }
  393.  
  394. sub check_root {
  395.  
  396. if ( $< != 0 ) {
  397. #print "WARNING:\n";
  398. #print "========\n";
  399. print "(This script is running without root privileges. ";
  400. print "Harddisk performance can only be measured with root privileges and therefore will be skipped.)\n";
  401. #print "\n";
  402. print "\n";
  403. }
  404. }
  405.  
  406. sub create_attachment_string {
  407.  
  408. foreach (@allfiles) {
  409. if (-e "$TMPDIR/$_") {
  410. $attachments .= "$TMPDIR/$_,";
  411. }
  412. }
  413.  
  414. $attachments = substr($attachments,0,-1);
  415. return $attachments;
  416.  
  417. }
  418.  
  419. sub anonymize_data {
  420.  
  421. # We do not need your IP or MAC addresses, therefore we filter them before uploading the files.
  422. # Serial Numbers of Equipment on the other hand might carry important
  423. # information, e.g. if hardware compatibility changes for certain serial numbers
  424. # Therefore, serial numbers are currently not filtered
  425.  
  426. foreach (@allfiles) {
  427. if (-e "$TMPDIR/$_") {
  428.  
  429. open FILE_OUT, ">", "$TMPDIR/$_.clean";
  430. open FILE, "<", "$TMPDIR/$_";
  431. while ($line = <FILE>) {
  432.  
  433. # Replace MAC adresses
  434. if ($line =~ m/(([0-9A-Fa-f]{2}:){5})[0-9A-Fa-f]{2}/) {
  435. $line =~ s/(([0-9A-Fa-f]{2}:){5})[0-9A-Fa-f]{2}/00:11:22:33:44:55/g;
  436. }
  437.  
  438. # Replace IP adresses, conservative filter used
  439. if ($line =~ m/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) {
  440. $line =~ s/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/192.168.0.0/g;
  441. }
  442.  
  443. #apparmor output can contain sensitive information (e.g. home directory file names)
  444. if ($line =~ m/apparmor=/) {
  445. $line = "FILTERED LINE (apparmor)";
  446. }
  447.  
  448. print FILE_OUT $line;
  449.  
  450.  
  451. }
  452. close FILE;
  453. close FILE_OUT;
  454.  
  455. #overwrite log files with anonymized one
  456. $old = "$TMPDIR/$_.clean";
  457. $new = "$TMPDIR/$_";
  458. rename ( $old, $new );
  459.  
  460. }
  461. }
  462.  
  463. return;
  464.  
  465. }
  466.  
  467. sub check_unifying_receivers {
  468. #ToDo: This information should be stored in a separate file
  469.  
  470. my $path_solaar = `which solaar-cli`;
  471. if ($path_solaar eq "") {
  472. # tool not installed
  473. } else {
  474.  
  475. system( 'echo "\n ------ solaar-cli output ------- \n" >> '.$TMPDIR.'/dmesg.txt');
  476. $cmd = substr($path_solaar,0,-1). " show >> $TMPDIR/dmesg.txt 2>> $TMPDIR/dmesg.txt ";
  477. system($cmd);
  478.  
  479. }
  480. }
  481.  
  482. sub check_graphics {
  483. #ToDo: This information should be stored in a separate file
  484.  
  485. my $path_xinput = `which xinput`;
  486. if ($path_xinput eq "") {
  487. # tool not installed
  488. } else {
  489. system( 'echo "\n ------ xinput ------- \n" >> '.$TMPDIR.'/dmesg.txt');
  490. $cmd = substr($path_xinput,0,-1). " >> $TMPDIR/dmesg.txt 2>> $TMPDIR/dmesg.txt ";
  491. system($cmd);
  492. }
  493.  
  494. system( 'echo "\n ------ Xorg.log ------- \n" >> '.$TMPDIR.'/dmesg.txt');
  495. $cmd = "cat /var/log/Xorg.0.log >> $TMPDIR/dmesg.txt 2>> $TMPDIR/dmesg.txt ";
  496. system($cmd);
  497.  
  498. }
  499.  
  500. sub check_drives {
  501. #ToDo: This information should be stored in a separate file
  502.  
  503. if (-e "/sys/block/") {
  504. system( 'echo "\n ------ ls -d /sys/block/s* ------- \n" >> '.$TMPDIR.'/dmesg.txt');
  505. $cmd0 = "ls -d /sys/block/s* >> $TMPDIR/dmesg.txt 2>> $TMPDIR/dmesg.txt ";
  506. system( 'echo "\n ------ /sys/block/s*/device/model ------- \n" >> '.$TMPDIR.'/dmesg.txt');
  507. $cmd1 = "cat /sys/block/s*/device/model >> $TMPDIR/dmesg.txt 2>> $TMPDIR/dmesg.txt ";
  508. system($cmd0);
  509. system($cmd1);
  510. }
  511.  
  512. }
  513.  
  514.  
  515. sub misc_stats {
  516.  
  517. # Adding some anonymous stats
  518.  
  519. system( 'echo "\n ------ STATS ------- \n" >> '.$TMPDIR.'/version.txt');
  520. system( 'echo "LHG_VERSION = '.$VERSION.' \n" >> '.$TMPDIR.'/version.txt');
  521. system( 'echo "UID = '.$uid.' \n" >> '.$TMPDIR.'/version.txt');
  522. system( 'echo "LHGUID = '.$USERID.' \n" >> '.$TMPDIR.'/version.txt');
  523. system( 'echo "LHGUIDDE = '.$USERIDDE.' \n" >> '.$TMPDIR.'/version.txt');
  524. system( 'echo "COMMENTID = '.$COMMENTID.' \n" >> '.$TMPDIR.'/version.txt');
  525. system( 'echo "USER_MAIL = '.$umail.' \n" >> '.$TMPDIR.'/version.txt');
  526. system( 'echo "\n ------ PCIDATA ------ \n" >> '.$TMPDIR.'/version.txt');
  527.  
  528. if (-e "/var/spool/lhg-tools/pcidata") {
  529. system( 'cat /var/spool/lhg-tools/pcidata >> '.$TMPDIR.'/version.txt');
  530. }
  531. system( 'echo "\n ------ USBDATA ------ \n" >> '.$TMPDIR.'/version.txt');
  532. if (-e "/var/spool/lhg-tools/usbdata") {
  533. system( 'cat /var/spool/lhg-tools/usbdata >> '.$TMPDIR.'/version.txt');
  534. }
  535.  
  536. }
  537.  
  538. sub create_lhg_configfile {
  539.  
  540. # create random uid
  541. my @chars = (0 .. 9, 'A'..'Z', 'a'..'z');
  542. my $rndstring;
  543. $rndstring .= $chars[rand @chars] for 1..16;
  544.  
  545. # use example domain as of RFC 2606
  546. my $dummymail = 'unknown@example.com';
  547.  
  548. open(my $fh, '>', $lhg_conf_filename) or return;
  549. print $fh <<"EOT";
  550. # This is the configuration file of the Linux-Hardware-Guide tools
  551.  
  552. # Every user gets an anonymous user id assigned to allow checking
  553. # his own upload results
  554. # ID is set by ANONYMOUS_USER_ID
  555.  
  556. ANONYMOUS_USER_ID = $rndstring
  557.  
  558. # Your account can be linked with your email address. This way we can
  559. # contact you, if we have questions regarding your uploaded hardware
  560. # data.
  561.  
  562. USER_EMAIL = $dummymail
  563.  
  564. EOT
  565. close($fh);
  566.  
  567. print "Configuration file '$lhg_conf_filename' created.\n";
  568. print "Please configure your email address if wanted or remove the file ";
  569. print "if not used in the future.\n";
  570.  
  571. return ($rndstring, $dummymail);
  572. }
  573.  
  574. sub check_configuration_file {
  575.  
  576. # If file does not exist try to create a new one
  577. if (! -e $lhg_conf_filename) {
  578. if ( $< == 0 ) {
  579. ($uid, $umail) = create_lhg_configfile();
  580. }
  581. return;
  582. }
  583.  
  584. # Read configuration from already existing file
  585. my %prefs = (
  586. 'ANONYMOUS_USER_ID' => "",
  587. 'USER_EMAIL' => ""
  588. );
  589. read_configfile($lhg_conf_filename, \%prefs);
  590.  
  591. # Set global variables for later usage
  592. $uid = $prefs{'ANONYMOUS_USER_ID'};
  593. $umail = $prefs{'USER_EMAIL'};
  594.  
  595. }
  596.  
  597. sub read_dmi {
  598.  
  599. system( 'echo "\n ------ DMI DATA ------ \n" >> '.$TMPDIR.'/dmesg.txt');
  600.  
  601. if ( $< == 0 ) {
  602. system ("dmidecode >> $TMPDIR/dmesg.txt");
  603. }
  604. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement