Skip to content

Instantly share code, notes, and snippets.

@ncalexan
Created October 22, 2020 17:22
Show Gist options
  • Save ncalexan/942b3c9e2ea4a8fbcd80431d8ba6e4c2 to your computer and use it in GitHub Desktop.
Save ncalexan/942b3c9e2ea4a8fbcd80431d8ba6e4c2 to your computer and use it in GitHub Desktop.
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Copied form
// taskcluster/docker/periodic-updates/scripts/getHSTSPreloadList.js,
// which simplified a version from
// browser/components/migration/MigrationUtils.jsm.
function spinResolve(promise) {
if (!(promise instanceof Promise)) {
return promise;
}
let done = false;
let result = null;
let error = null;
promise
.catch(e => {
dump(e);
console.log(`e: ${e}`, e, e.stack);
error = e;
})
.then(r => {
result = r;
done = true;
});
let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
tm.spinEventLoopUntil(() => done);
if (error) {
throw error;
} else {
return result;
}
}
const { Services } = ChromeUtils.import(
"resource://gre/modules/Services.jsm"
);
const { Subprocess } = ChromeUtils.import(
"resource://gre/modules/Subprocess.jsm"
);
async function schedule() {
let uid = 501; // XXX
let file = Services.dirsvc.get("Home", Ci.nsIFile);
file.append("Library");
file.append("LaunchAgents");
// file.append("MOZ_MACBUNDLE_ID");
let MOZ_MACBUNDLE_ID = "org.mozilla.firefoxnightly"; // TODO.
let hash = "1"; // TODO: differentiate installations. Hash of `UpdRootD`, perhaps?
let short_label = "update";
let long_label = `${MOZ_MACBUNDLE_ID}.${hash}.backgroundtask.${short_label}`;
file.append(`${long_label}.plist`);
const doc = new DOMParser().parseFromString("<plist></plist>", "text/xml");
const root = doc.documentElement;
root.setAttribute("version", "1.0");
// <?xml version="1.0" encoding="UTF-8"?>
// <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
// "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
// <plist version="1.0">
// <dict>
// <key>Label</key>
// <string>MyAgent</string>
// <key>ProgramArguments</key>
// <array>
// <string>/path/to/script/or/program</string>
// </array>
// </dict>
// </plist>
let dict = doc.createElement("dict");
root.appendChild(dict);
let key = doc.createElement("key");
key.textContent = "Label";
dict.appendChild(key);
let string = doc.createElement("string");
let MOZ_APP_DISPLAYNAME = "MOZ_APP_DISPLAYNAME"; // TODO
string.textContent = long_label; // "${MOZ_APP_DISPLAYNAME} ${label} background task";
dict.appendChild(string);
key = doc.createElement("key");
key.textContent = "ProgramArguments";
dict.appendChild(key);
let array = doc.createElement("array");
dict.appendChild(array);
for (let x of [Services.dirsvc.get("XREExeF", Ci.nsIFile).path,
"--xpcshell",
"-e",
"dump('test\\n')"]) {
let string = doc.createElement("string");
string.textContent = x;
array.appendChild(string);
}
const de = Ci.nsIDocumentEncoder;
let flags = de.OutputLFLineBreak | de.OutputFormatted; // TODO: figure out why this isn't wrapping.
let enc = Cu.createDocumentEncoder("text/xml");
enc.setCharset("UTF-8");
enc.init(doc, "text/xml", flags);
// Ci.nsIDocumentEncoder.OutputLFLineBreak | Ci.nsIDocumentEncoder.OutputFormatted);
let plist = `<?xml version="1.0" encoding="UTF-8"?>\n`;
plist += `<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n`;
plist += enc.encodeToString();
dump(plist);
await IOUtils.writeAtomic(file.path, new TextEncoder().encode(plist));
console.log(`path: ${file.path}`);
let bootout = await Subprocess.call({
command: "/bin/launchctl",
arguments: ["bootout", `gui/${uid}`, file.path],
environment: {},
stderr: "stdout",
});
let stdout = await bootout.stdout.readString();
console.log(stdout);
// let { exitCode } =
await bootout.wait();
let bootstrap = await Subprocess.call({
command: "/bin/launchctl",
arguments: ["bootstrap", `gui/${uid}`, file.path],
environment: {},
stderr: "stdout",
});
stdout = await bootstrap.stdout.readString();
console.log(stdout);
let { exitCode } = await bootstrap.wait();
if (exitCode != 0) {
throw new Components.Exception(
`Failed to run launchctl: ${exitCode}`,
Cr.NS_ERROR_UNEXPECTED
);
}
}
spinResolve(schedule().then(console.log));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment