Author: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed Dec 03 15:06:09 2025 +0000 Add documentation for Windows Service setup Adds src/builder/README.Windows.md detailing how to build, create the user, grant necessary privileges, and register sai-builder as a Windows Service. diff --git a/READMEs/README-build-windows.md b/READMEs/README-build-windows.md index 96b4135..bcf1abd 100644 --- a/READMEs/README-build-windows.md +++ b/READMEs/README-build-windows.md @@ -58,3 +58,76 @@ As administrator Then create the config JSON in `\ProgramData\sai\builder\conf`, using the platform name `windows-10`. + +## Running as a service with sai user + + +## Building + +Building on Windows follows the standard CMake flow. You can perform the build as a standard user. + +```cmd +cd sai +mkdir build +cd build +cmake .. +cmake --build . --config Debug +``` + +Then in a Command Prompt with Administrator rights + +```cmd +cd "\User\your-build-user\sai\build" +cmake --install . --config Debug +``` + +This will collect the needed files into "C:\Program Files (x86)\sai\bin" + +## Setup for Service Execution + +To run `sai-builder` as a Windows Service under a specific unprivileged user (e.g., `.\sai`), follow these steps. + +### 1. Create the User + +If the `sai` user does not exist, create it. Open a Command Prompt **as Administrator**. + +```cmd +net user sai /add /passwordchg:no /expires:never +``` + +### 2. Grant Privileges + +The `sai` user needs permission to: +1. **Log on as a Service**: Windows usually grants this automatically when you register the service with a specific user, but you can check it in `secpol.msc`. +2. **Shut down the system**: If you want the builder to handle power management (suspend/shutdown). + +To grant rights: +1. Run `secpol.msc` (Local Security Policy). +2. Go to **Local Policies** -> **User Rights Assignment**. +3. Find **Shut down the system**. +4. Double-click, click **Add User or Group**, type `sai`, check Names, and OK. +5. (Optional) Ensure **Log on as a service** also includes `sai`. + +### 3. Install the Service + +Open a Command Prompt **as Administrator** (Right-click Start -> Command Prompt (Admin) or PowerShell (Admin)). + +Run the following `sc create` command. **Note:** The space after the `=` sign in options like `binPath=` is mandatory. + +Replace the paths and password with your actual values. + +```cmd +sc create SaiBuilder binPath= "c:\Program Files (x86)\sai\bin\sai-builder.exe --service" DisplayName= "Sai Builder" start= auto obj= ".\sai" password="password-for-sai-account" +``` + +### 4. Manage the Service + +You can now start and stop the builder using standard Windows commands (Administrator required): + +```cmd +sc start SaiBuilder +sc stop SaiBuilder +``` + +To view logs or status, check the standard `sai-builder` logs (configured in your JSON config) or the Event Viewer (Application log) if the service fails to start immediately. + diff --git a/assets/freebsd.svg b/assets/freebsd.svg new file mode 100644 index 0000000..fba9b89 --- /dev/null +++ b/assets/freebsd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/sai.js b/assets/sai.js index 1741428..9f545f0 100644 --- a/assets/sai.js +++ b/assets/sai.js @@ -1621,7 +1621,13 @@ function ws_open_sai() // console.log(msg.data); // if (msg.data.length < 10) // return; + try { jso = JSON.parse(msg.data); + } catch { + console.log("Bad JSON received:"); + console.log(msg.data); + return + } // console.log(jso.schema); if (jso.alang) { diff --git a/src/builder/CMakeLists.txt b/src/builder/CMakeLists.txt index 8768b0d..ee680f4 100644 --- a/src/builder/CMakeLists.txt +++ b/src/builder/CMakeLists.txt @@ -82,6 +82,14 @@ if (requirements) target_link_libraries(${SUB} ws2_32.lib userenv.lib psapi.lib iphlpapi.lib) endif() + if (MSVC OR WIN32) + # Auto-copy dependencies (DLLs) during install + install(CODE " + include(BundleUtilities) + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/${BIN_DIR}/sai-builder.exe\" \"\" \"\") + " COMPONENT builder) + endif() + install(TARGETS "sai-builder" RUNTIME DESTINATION "${BIN_DIR}" COMPONENT builder) diff --git a/src/builder/b-task.c b/src/builder/b-task.c index 5eadab8..ea575ea 100644 --- a/src/builder/b-task.c +++ b/src/builder/b-task.c @@ -90,6 +90,7 @@ const char *git_helper_sh = const char *git_helper_bat = "@echo on\n" "setlocal EnableDelayedExpansion\n" + "set \"PATH=%PATH%;C:\\Program Files\\Git\\cmd;C:\\Windows\\System32;C:\\Windows\"\n" "echo \"git_helper_bat: starting\"\n" "set \"OPERATION=%~1\"\n" "echo \"OPERATION: !OPERATION!\"\n" @@ -103,11 +104,10 @@ const char *git_helper_bat = " echo \"HASH: !HASH!\"\n" " echo \"MIRROR_PATH: !MIRROR_PATH!\"\n" " :lock_wait\n" - " mkdir \"!MIRROR_PATH!.lock\" 2>nul\n" - " mkdir \"!MIRROR_PATH!\" 2>nul\n" + " mkdir \"!MIRROR_PATH!.lock\"\n" " if errorlevel 1 (\n" " echo \"git mirror locked, waiting...\"\n" - " C:\\Windows\\System32\\timeout.exe /t 1 /nobreak > nul\n" + " ping -n 2 127.0.0.1 >nul\n" " goto :lock_wait\n" " )\n" " if exist \"!MIRROR_PATH!\\\\.git\" (\n" diff --git a/src/builder/b-win32-service.c b/src/builder/b-win32-service.c index aacab5d..e290e94 100644 --- a/src/builder/b-win32-service.c +++ b/src/builder/b-win32-service.c @@ -20,7 +20,7 @@ */ #if defined(WIN32) - +#include #include #include #include diff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt index e91bf34..0f413fc 100644 --- a/src/web/CMakeLists.txt +++ b/src/web/CMakeLists.txt @@ -149,6 +149,7 @@ if (requirements) ../../assets/virt-qemu.svg ../../assets/w10.svg ../../assets/w11.svg + ../../assets/freebsd.svg DESTINATION "${DATA_DIR}/sai/assets" COMPONENT web) diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c index b6d0804..8570e0e 100644 --- a/src/web/w-ws-browser.c +++ b/src/web/w-ws-browser.c @@ -950,7 +950,7 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss) lws_json_purify(esc1, pss->auth_user, sizeof(esc1) - 1, &iu) ); - saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(vhd, start, + saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start, lws_ptr_diff_size_t(p, start), lws_write_ws_flags(LWS_WRITE_TEXT, 1, 0)); p = start; @@ -1007,7 +1007,7 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss) p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), "{\"e\":"); if (lws_ptr_diff_size_t(end, p) < 256) { - saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(vhd, start, + saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start, lws_ptr_diff_size_t(p, start), lws_write_ws_flags(LWS_WRITE_TEXT, 0, 0)); p = start;