::namespace eval ::qw::taskutil {} ::proc ::qw::taskutil::windows_tasklist {} { # ------------------------------------------------------------ # Return a list of task structures. # ------------------------------------------------------------ /* { Uses the windows tasklist command to retrieve a list of running processes. The tasklist is returned as a list of sargs in the form: { .image_name TeamViewer_Service.exe .pid 10508 .session_name Services .session_number 0 .mem_usage 4280 } ... { .image_name TeamViewer.exe .pid 8372 .session_name Console .session_number 1 .mem_usage 6976 } */ } ::if {$::tcl_platform(platform) ne "windows"} { ::qw::bug 314120200129114748 "windows tasklist called on invalid platform $::tcl_platform(platform)."; } ::set rwb1_debug 0; ::if {$rwb1_debug} {::puts "rwb1_debug,::qw::taskutil::windows_tasklist,1000.0";} ::qw::profile::begin "windows_tasklist_load"; ::set SrcTaskList [::exec tasklist]; ::qw::profile::end "windows_tasklist_load"; ::set DstTaskList [::sargs]; ::foreach Line [::split $SrcTaskList "\n"] { ::set Line [::string trim $Line]; ::if {[::llength $Line]==0} { # empty line ::continue; } ::if {[::string first "Image Name" $Line]>=0} { # title line ::continue; } ::if {[::string first "=============" $Line]>=0} { # separator ::continue; } ::set Structure [::sargs]; ::sargs::var::set Structure .image_name [::string trim [::string range $Line 0 24]]; ::sargs::var::set Structure .pid [::string trim [::string range $Line 26 33]]; ::sargs::var::set Structure .session_name [::string trim [::string range $Line 35 50]]; ::sargs::var::set Structure .session_number [::string trim [::string range $Line 52 62]]; ::sargs::var::set Structure .mem_usage [::string map [::list "," ""] [::string trim [::string range $Line 64 73]]]; ::lappend DstTaskList $Structure; } ::if {$rwb1_debug} { ::set DstPath [::file join $::qw_program_folder windows_tasklist.txt] ::set Handle [::open $DstPath w+]; ::qw::finally [::list ::close $Handle]; ::foreach Item $DstTaskList { ::puts $Handle "{"; ::puts $Handle [::sargs::format $Item]; ::puts $Handle "}"; } # ::qw::fileutil::file_write .path $DstPath .data $DstTaskList; ::puts "rwb1_debug,windows tasklist written to $DstPath."; } ::return $DstTaskList; } /* { Example output from a windows tasklst command: Image Name PID Session Name Session# Mem Usage ========================= ======== ================ =========== ============ System Idle Process 0 Services 0 24 K System 4 Services 0 644 K smss.exe 380 Services 0 104 K csrss.exe 556 Services 0 1,080 K wininit.exe 624 Services 0 236 K csrss.exe 632 Console 1 11,468 K winlogon.exe 680 Console 1 320 K services.exe 728 Services 0 4,996 K lsass.exe 736 Services 0 6,668 K lsm.exe 752 Services 0 1,636 K svchost.exe 840 Services 0 3,648 K svchost.exe 924 Services 0 4,656 K svchost.exe 1020 Services 0 18,152 K svchost.exe 512 Services 0 241,236 K svchost.exe 172 Services 0 8,572 K svchost.exe 588 Services 0 22,932 K svchost.exe 1176 Services 0 47,244 K svchost.exe 1340 Services 0 4,488 K spoolsv.exe 1496 Services 0 4,232 K armsvc.exe 1636 Services 0 624 K svchost.exe 1692 Services 0 628 K svchost.exe 1976 Services 0 4,836 K svchost.exe 2044 Services 0 692 K LogiRegistryService.exe 1300 Services 0 644 K mbamscheduler.exe 1380 Services 0 5,504 K mysqld.exe 2112 Services 0 1,108 K ccSvcHst.exe 2184 Services 0 16,496 K sqlwriter.exe 2288 Services 0 596 K svchost.exe 2312 Services 0 608 K WmiPrvSE.exe 2980 Services 0 7,676 K Smc.exe 3548 Services 0 7,472 K unsecapp.exe 3944 Services 0 308 K taskhost.exe 2528 Console 1 7,488 K ccSvcHst.exe 2492 Console 1 4,852 K svchost.exe 3156 Services 0 1,068 K dwm.exe 4760 Console 1 1,364 K explorer.exe 4784 Console 1 34,948 K RAVCpl64.exe 4928 Console 1 1,564 K hkcmd.exe 5048 Console 1 344 K igfxpers.exe 5080 Console 1 332 K LCore.exe 5108 Console 1 5,736 K googledrivesync.exe 3996 Console 1 116 K BingSvc.exe 4340 Console 1 2,688 K Snagit32.exe 4376 Console 1 29,040 K WZQKPICK.EXE 4448 Console 1 160 K IAStorIcon.exe 4136 Console 1 6,812 K jusched.exe 4476 Console 1 2,340 K googledrivesync.exe 1012 Console 1 5,832 K SnagPriv.exe 3444 Console 1 128 K soffice.exe 4680 Console 1 108 K soffice.bin 1236 Console 1 4,748 K mobsync.exe 3524 Console 1 504 K logitechg_discord.exe 820 Console 1 7,920 K TscHelp.exe 3732 Console 1 332 K IAStorDataMgrSvc.exe 4796 Services 0 7,872 K NASvc.exe 2100 Services 0 424 K SnagitEditor.exe 5576 Console 1 9,248 K TrustedInstaller.exe 2300 Services 0 876 K xplorer2_64.exe 3436 Console 1 38,628 K cmd.exe 5768 Console 1 1,928 K conhost.exe 4672 Console 1 1,164 K cmd.exe 4864 Console 1 404 K conhost.exe 1544 Console 1 748 K cmd.exe 3704 Console 1 1,792 K conhost.exe 6284 Console 1 1,132 K cmd.exe 6116 Console 1 144 K conhost.exe 3220 Console 1 324 K cmd.exe 6848 Console 1 148 K conhost.exe 7116 Console 1 288 K taskhost.exe 9144 Console 1 596 K cmd.exe 3216 Console 1 112 K conhost.exe 8020 Console 1 304 K cmd.exe 8420 Console 1 152 K conhost.exe 1532 Console 1 436 K hh.exe 10056 Console 1 3,888 K hh.exe 9360 Console 1 2,056 K cmd.exe 9044 Console 1 148 K conhost.exe 10252 Console 1 392 K SSScheduler.exe 10964 Console 1 532 K cmd.exe 8916 Console 1 188 K conhost.exe 9264 Console 1 284 K WINWORD.EXE 3024 Console 1 5,700 K splwow64.exe 8124 Console 1 11,564 K cmd.exe 1392 Console 1 188 K conhost.exe 11040 Console 1 300 K LINK.EXE 8356 Console 1 116 K cmd.exe 2880 Console 1 184 K conhost.exe 4616 Console 1 292 K firefox.exe 7152 Console 1 291,036 K firefox.exe 8532 Console 1 157,232 K firefox.exe 3448 Console 1 18,424 K firefox.exe 3756 Console 1 24,224 K AdobeARM.exe 6036 Console 1 1,364 K firefox.exe 3112 Console 1 14,020 K SearchIndexer.exe 10612 Services 0 3,940 K firefox.exe 8576 Console 1 64,420 K firefox.exe 5680 Console 1 12,192 K cmd.exe 2124 Console 1 312 K conhost.exe 10464 Console 1 320 K cmd.exe 8636 Console 1 192 K conhost.exe 5436 Console 1 320 K TeamViewer_Service.exe 10508 Services 0 3,600 K TeamViewer.exe 8372 Console 1 6,764 K tv_w32.exe 7848 Console 1 480 K tv_x64.exe 1832 Console 1 392 K cmd.exe 4016 Console 1 140 K conhost.exe 9820 Console 1 336 K firefox.exe 5224 Console 1 15,904 K EXCEL.EXE 6560 Console 1 240 K filezilla.exe 7148 Console 1 1,252 K fzsftp.exe 10216 Console 1 328 K conhost.exe 8796 Console 1 176 K firefox.exe 8224 Console 1 86,560 K firefox.exe 9520 Console 1 43,056 K firefox.exe 7456 Console 1 116,328 K firefox.exe 5484 Console 1 107,064 K hh.exe 7372 Console 1 2,200 K hh.exe 7924 Console 1 2,216 K DbxSvc.exe 3040 Services 0 636 K Dropbox.exe 9068 Console 1 89,668 K Dropbox.exe 1732 Console 1 700 K Dropbox.exe 9888 Console 1 1,608 K QtWebEngineProcess.exe 8368 Console 1 3,204 K QtWebEngineProcess.exe 6288 Console 1 22,100 K QtWebEngineProcess.exe 6684 Console 1 25,248 K tclkit-win32-sh.upx.exe 9320 Console 1 104 K mmc.exe 10764 Console 1 23,472 K hh.exe 7136 Console 1 1,944 K thunderbird.exe 10432 Console 1 175,936 K wish.exe 8200 Console 1 13,036 K svchost.exe 6840 Services 0 308 K AcroRd32.exe 6416 Console 1 1,040 K AcroRd32.exe 9116 Console 1 5,584 K RdrCEF.exe 10868 Console 1 6,988 K RdrCEF.exe 1768 Console 1 3,640 K jucheck.exe 10156 Console 1 564 K bcw.exe 5252 Console 1 48,644 K taskeng.exe 6972 Services 0 5,904 K taskhost.exe 2524 Services 0 12,892 K taskeng.exe 3644 Console 1 6,528 K audiodg.exe 8744 Services 0 17,604 K nv2_dt.exe 6956 Console 1 31,900 K tasklist.exe 6584 Console 1 6,076 K */ } ::proc ::qw::taskutil::task_is_scheduled {sargs} { ::set rwb1_debug 0; ::if {$rwb1_debug} {::puts "rwb1_debug,task_is_scheduled,1000.0,sargs==$sargs";} ::set TaskName [::sargs::get $sargs .task_name]; ::if {$TaskName eq ""} { ::qw::bug 314120250205095247 "[::namespace current]::[::qw::methodname] - invalid name \"$TaskName\"."; } ::switch -- $::tcl_platform(platform) { "windows" { ::if {$rwb1_debug} {::puts "rwb1_debug,task_is_scheduled,1000.1,tasks==\n[::exec schtasks /query]";} ::if {[::string first $TaskName [::exec schtasks /query]]<0} { ::if {$rwb1_debug} {::puts "rwb1_debug,task_is_scheduled,1000.2";} ::return 0; } ::if {$rwb1_debug} {::puts "rwb1_debug,task_is_scheduled,1000.3";} ::return 1; } "unix" { ::qw::bug 314120250205095151 "[::namespace current]::[::qw::methodname] - not implemented for linux."; } } } ::proc ::qw::taskutil::task_make_scheduled {sargs} { # ------------------------------------------------------------ # Use schtasks or cron to schedule a task, if not already scheduled. # ------------------------------------------------------------ /* { Usage: ::qw::taskutil::task_make_scheduled \ .task_name $TaskName \ .task_executable $Executable \ .minutes $Minutes \ ; Taskname should be highly unique to avoid conflicts. Executable is command line to launch command, usually including a -script argument. Minutes is the number of minutes between invocations. We'll add other unit types if it comes up.q */ } ::set rwb1_debug 0; ::set TaskName [::sargs::get $sargs .task_name]; ::if {$TaskName eq ""} { ::qw::bug 314120250204174024 "[::namespace current]::[::qw::methodname] - invalid name \"$TaskName\"."; } ::set TaskExecutable [::sargs::get $sargs .task_executable]; ::if {$TaskExecutable eq ""} { ::qw::bug 314120250204174025 "[::namespace current]::[::qw::methodname] - invalid executable \"$TaskExecutable\"."; } ::set Minutes [::sargs::get $sargs .minutes]; ::if {$Minutes eq ""} { ::qw::bug 314120250204174026 "[::namespace current]::[::qw::methodname] - invalid interval \"$Minutes\"."; } ::if {[::qw::taskutil::task_is_scheduled $sargs]} { # ------------------------------------------------------------ # task already scheduled. Nothing to do. # ------------------------------------------------------------ ::if {$rwb1_debug} {::puts "rwb1_debug,task_make_scheduled,1000.0,task_name==$TaskName";} ::return; } ::switch -- $::tcl_platform(platform) { "windows" { ::set Commandline "schtasks /create /sc minute /mo $Minutes /tn $TaskName /tr \"$TaskExecutable\""; ::if {$rwb1_debug} {::puts "rwb1_debug,task_make_scheduled,1000.0,commandline==$Commandline";} ::set Result [::eval ::exec $Commandline]; ::return; } "unix" { /* { On linux/unix we use cron/crontab to schedule the message_database_stub. */ } # ------------------------------------------------------------ # Get crontab data and see if message_database_stub already scheduled. # ------------------------------------------------------------ ::if {!$_cron_is_enabled} { ::return; } ::qw::try { ::if {$rwb1_debug} {::puts "rwb1_debug,message_database_checker_task_schedule,1000.1";} ::set CrontabData [::exec -- crontab -l]; ::if {$rwb1_debug} {::puts "rwb1_debug,message_database_checker_task_schedule,1000.2";} } catch Exception { ::if {$rwb1_debug} {::puts "rwb1_debug,message_database_checker_task_schedule,1000.3,Exception==\n$Exception";} ::set CrontabData $Exception; } ::if {$rwb1_debug} {::puts "rwb1_debug,message_database_checker_task_schedule,crontab,1000.4,CrontabData==\n$CrontabData";} ::if {[::string first "qw_message_database_checker" $CrontabData]>=0} { # ------------------------------------------------------------ # message_database_checker already scheduled. Nothing to do. # ------------------------------------------------------------ ::if {$rwb1_debug} {::puts "rwb1_debug,message_database_checker_task_schedule,1000.5";} ::return; } ::set DstPath [::file join $::qw_program_folder qw_message_database_checker.exe]; ::qw::fileutil::create_duplicate_executable .destination_path $DstPath; ::if {$rwb1_debug} {::puts "rwb1_debug,message_database_checker_task_schedule,1000.6";} ::if {[::string match "*can't open*" $CrontabData]||[::string match "*no crontab*" $CrontabData]} { # ------------------------------------------------------------ # crontab is empty or doesn't exist. # ------------------------------------------------------------ ::if {$rwb1_debug} {::puts "rwb1_debug,message_database_checker_task_schedule,1000.7";} ::set CrontabData ""; } ::if {$rwb1_debug} {::puts "rwb1_debug,message_database_checker_task_schedule,1000.8";} # ------------------------------------------------------------ # Build crontab line. # ------------------------------------------------------------ ::set StubProgramPath [::file join $::qw_program_folder qw_message_database_checker.exe]; ::set StubScriptPath "message_database/qw_message_database_stub.qw_script"; ::set User [::exec whoami]; ::if {[::info exists ::env(DISPLAY)]} { ::append CrontabData "*/$::qw::control(service_stub_checker_cron_interval) * * * * DISPLAY=$::env(DISPLAY) $StubProgramPath -script $StubScriptPath\n"; } else { ::append CrontabData "*/$::qw::control(service_stub_checker_cron_interval) * * * * DISPLAY=:0 $StubProgramPath -script $StubScriptPath\n"; } ::set Count 0; ::while {1} { # ------------------------------------------------------------ # Find unused tmp file name. # ------------------------------------------------------------ /* { We can only set the crontab by copying a file to it. So here we take existing cron data, append to it, write it to a tmp file, and call crontab with that file path. */ } ::set TmpFile [::file join $::qw_program_folder crontab$Count.tmp]; ::if {![::file exists $TmpFile]} { ::break; } ::incr Count 1; } # ------------------------------------------------------------ # write new crontab data to tmp file. # ------------------------------------------------------------ ::if {$rwb1_debug} {::puts "rwb1_debug,message_database_checker_task_schedule,1000.9";} ::set Handle [::open $TmpFile w+]; ::puts $Handle $CrontabData; ::close $Handle; ::qw::finally [::list ::file delete $TmpFile]; # ------------------------------------------------------------ # Set crontab data from tmp file. # ------------------------------------------------------------ ::if {$rwb1_debug} {::puts "rwb1_debug,message_database_checker_task_schedule,1000.10";} ::exec -- crontab -u $User $TmpFile; ::if {$rwb1_debug} {::puts "rwb1_debug,message_database_checker_task_schedule,1000.11";} } } } ::proc ::qw::taskutil::task_make_unscheduled {sargs} { # ------------------------------------------------------------ # Cause schtasks or cron to unschedule a task, if it is scheduled. # ------------------------------------------------------------ ::set rwb1_debug 0; ::set TaskName [::sargs::get $sargs .task_name]; ::if {$TaskName eq ""} { ::qw::bug 314120250204174030 "[::namespace current]::[::qw::methodname] - invalid name \"$TaskName\"."; } ::if {![::qw::taskutil::task_is_scheduled $sargs]} { # ------------------------------------------------------------ # task not scheduled. Nothing to do. # ------------------------------------------------------------ ::return; } ::switch -- $::tcl_platform(platform) { "windows" { ::if {$rwb1_debug} {::puts "rwb1_debug,task_make_unscheduled,1000.3,sargs==$sargs";} ::qw::try { ::exec schtasks /delete /tn $TaskName /f; } catch Exception { ::qw::warning 314120250331174042 "Can't find task \"$TaskName\", exception==$Exception"; } ::if {$rwb1_debug} {::puts "rwb1_debug,task_make_unscheduled,1000.99";} ::return; } "unix" { /* { We unschedule the message_database_stub by removing a line from crontab, if it is found there. */ } # ------------------------------------------------------------ # Get crontab data and see if message_database_stub already scheduled. # ------------------------------------------------------------ ::qw::try { ::set CrontabData [::exec -- crontab -l]; } catch Exception { # ------------------------------------------------------------ # Exception implies no crontab so nothing to do. # ------------------------------------------------------------ ::if {$rwb1_debug} {::puts "rwb1_debug,crontab_unschedule,2000.0,Exception==\n$Exception";} ::set CrontabData $Exception; ::return; } ::if {$rwb1_debug} {::puts "rwb1_debug,crontab_unschedule,2000.0,CrontabData==\n$CrontabData";} ::if {[::string first "qw_message_database_checker" $CrontabData]<0} { # ------------------------------------------------------------ # message_database_checker not scheduled so nothing to do. # ------------------------------------------------------------ ::if {$rwb1_debug} {::puts "rwb1_debug,crontab_unschedule,2000.1";} ::return; } # ------------------------------------------------------------ # Create NewCrontabData containing everything but message_database_stub line. # ------------------------------------------------------------ ::set LineList [::split $CrontabData "\n"]; ::set NewCrontabData ""; ::foreach Line $LineList { ::if {[::string first "qw_message_database_checker" $Line]<0&&[::string trim $Line] ne ""} { ::append NewCrontabData "$Line\n"; } } ::if {$rwb1_debug} {::puts "rwb1_debug,crontab_unschedule,2000.2";} ::set Count 0; ::while {1} { # ------------------------------------------------------------ # Find unused tmp file name. # ------------------------------------------------------------ /* { We can only set the crontab by calling crontab with a file path. */ } ::set TmpFile /home/benn/nv/cron$Count.tmp; ::if {![::file exists $TmpFile]} { ::break; } ::incr Count 1; } # ------------------------------------------------------------ # write new crontab data to tmp file. # ------------------------------------------------------------ ::if {$rwb1_debug} {::puts "rwb1_debug,crontab_unschedule,2000.5";} ::set Handle [::open $TmpFile w+]; ::puts $Handle $NewCrontabData; ::close $Handle; ::qw::finally [::list ::file delete $TmpFile]; # ------------------------------------------------------------ # Set crontab data from tmp file. # ------------------------------------------------------------ ::if {$rwb1_debug} {::puts "rwb1_debug,crontab_unschedule,2000.6";} ::exec -- crontab -u [::exec whoami] $TmpFile; # ::exec -- crontab $TmpFile; ::if {$rwb1_debug} {::puts "rwb1_debug,crontab_unschedule,2000.7";} } } }