Search code examples
xmlwindowsxml-parsingbatch-processingboinc

Boinc : a Windows batch file to resume suspended tasks with the scheduler


Requesting some magic from Windows batch script wizards... :D

For competition purposes, members of my team use BoincTasks to automatically suspend Boinc workunits at 99% completion, thus they can resume them after a starting dateline, sending a lot of results in a short time. Unfortunately, BoincTasks can automatically suspend tasks, but can't resume them automatically. So : in case the user can't stay in front of the computer at a certain date/time to manually resume tasks, it would be nice to have a Windows batch script being able to do the job, triggered by the Windows scheduler.

Boinc uses a (false) .XML file to keep tracks of any task he manages. Usually, you'll find it here : C:\ProgramData\BOINC\client_state.xml

Here's the interesting part of it (full file is 368k) :

<active_task>
   <project_master_url>http://asteroidsathome.net/boinc/</project_master_url>
   <result_name>ps_170405_input_161867_8_1</result_name>
   <active_task_state>1</active_task_state>
   ...
</active_task>

There's an "active_task" tag for each task. Suspended tasks have the "active_task_state" subtag set to "0".

Boinc has a command line executable that can resume a single task, presuming you know some parameters. Here's an example :

boinccmd --task [project_master_url] [result_name] resume

So, I'd like to write a batch script being able to resume suspended tasks from a chosen "project_master_url". Here's the ideal scenario : I'd like to resume all the tasks from the "asteroidsathome" Boinc project (see the url in the code above).

  1. Open and parse "C:\ProgramData\BOINC\client_state.xml" ;
  2. find all the "active_task" branches having "project_master_url"=="the project url" AND "active_task_state"==0 ;
  3. for each task, execute the command line, as stated above.

I'm quite used to Linux .sh and PHP scripts, but I just do not understand how to obtain the same result in Windows batch. I've found how to parse the XML using this : parsing xml from batch ... But the "filtering/throwing the extracted data to a command line" process remains a mystery :(

Some spells to cast ? Thanks :D


Solution

  • OK. I managed to do it... It works :) I'm pretty sure there's a LOT of room for improvements. Feel free to suggest a better way to do it

    @echo off
    setlocal enableextensions EnableDelayedExpansion
    
    rem Settings
    rem ==============================================================
    rem Defines the url of the project we want the tasks to be resumed
    set ResumeProjectUrl=https://wuprop.boinc-af.org/
    rem ==============================================================
    rem Defines the path to the client_state.xml Boinc file
    set ClientStateFile=%ProgramData%\BOINC\client_state.xml
    rem Defines the path to the boinccmd.exe
    set BoincCmdFile=%ProgramFiles%\BOINC\boinccmd.exe
    
    rem This is real content, extracted from client_state.xml
    rem (useful to have a quick reference)
    rem    <project_master_url>https://wuprop.boinc-af.org/</project_master_url>
    rem    <result_name>data_collect_v4_1490525102_609550_0</result_name>
    rem    <active_task_state>1</active_task_state>
    
    rem Find the lines that contains a specific string... "project_master_url" seems OK (1st line in the above example)
    for /f "delims=:" %%I in ('findstr.exe /I /L /N /C:"project_master_url" "%ClientStateFile%" 2^>nul') do (
    
        rem assign variables :
        rem a is the first line number, containing <project_master_url>value</project_master_url>
        rem b is the next line number, containing <result_name>value</result_name>
        rem c is the next line number, containing <active_task_state>value</active_task_state>
        set /a a=%%I
        set /a b=a+1
        set /a c=a+2
    
        rem As we want to resume only a specific project WUs, let's evaluate the url first
        rem read first line (a)
        for /f "tokens=1* delims=]" %%A in ('^<"%ClientStateFile%" FIND /N /V "" ^| FINDSTR /B /C:"[!a!]"') do (
    
            rem extract project_master_url value (between <tag></tag>)
            rem %%a is opening tag, %%b is the value, %%c is closing tag
            for /f  "tokens=2-4delims=<>" %%a in ("%%B") do (
    
                rem Let's compare the project_master_url value and the desired url (in Settings)
                if "%ResumeProjectUrl%"=="%%b" (
    
                    rem If values are the same let's find if <active_task_state> is 0 (suspended)
                    rem Read third line (c)
                    for /f "tokens=1* delims=]" %%A in ('^<"%ClientStateFile%" FIND /N /V "" ^| FINDSTR /B /C:"[!c!]"') do (
    
                        rem extract active_task value (between <tag></tag>)
                        rem %%a is opening tag, %%b is the value, %%c is closing tag
                        for /f  "tokens=2-4delims=<>" %%a in ("%%B") do (
    
                            rem Value is 0 (suspended)
                            if "%%b"=="0" (
    
                                rem Read second line (b)
                                for /f "tokens=1* delims=]" %%A in ('^<"%ClientStateFile%" FIND /N /V "" ^| FINDSTR /B /C:"[!b!]"') do (
    
                                    rem extract result_name value (between <tag></tag>)
                                    rem %%a is opening tag, %%b is the value, %%c is closing tag
                                    for /f  "tokens=2-4delims=<>" %%a in ("%%B") do (
    
                                        rem let's call the boinccmd executable to resume the task
                                        start "Resuming" /B "%BoincCmdFile%" --task %ResumeProjectUrl% %%b resume
                                    )
                                )
                            )
    rem                     if "%%b" NEQ "0" (
    rem                         echo "Good project, but the task is active"
    rem                     )
                        )
                    )
                )
    rem         if "%ResumeProjectUrl%" NEQ "%%b" (
    rem             echo "Other project"
    rem         )
            )
        )
    
        rem clear variables
        set a=
        set b=
        set c=
    
    )
    endlocal