Repository: dnschneid/crouton Branch: master Commit: 59e36db17c54 Files: 165 Total size: 608.4 KB Directory structure: gitextract_imr3a977/ ├── .github/ │ ├── CONTRIBUTING.md │ └── ISSUE_TEMPLATE.md ├── .gitignore ├── AUTHORS ├── CONTRIBUTORS ├── LICENSE ├── Makefile ├── README.md ├── README.zh.md ├── build/ │ ├── CONTRIBUTORS.sed │ ├── genversion.sh │ ├── release.sh │ └── wrapper.sh ├── chroot-bin/ │ ├── brightness │ ├── crouton-noroot │ ├── crouton-unity-autostart │ ├── croutonclip │ ├── croutoncycle │ ├── croutonfindnacl │ ├── croutonnotify │ ├── croutonpowerd │ ├── croutontriggerd │ ├── croutonurlhandler │ ├── croutonversion │ ├── croutonxinitrc-wrapper │ ├── gnome-session-wrapper │ ├── host-dbus │ ├── host-wayland │ ├── setres │ ├── startgnome │ ├── startunity │ ├── volume │ ├── xinit │ └── xiwi ├── chroot-etc/ │ ├── kodi-cycle.py │ ├── kodi-keyboard.xml │ ├── pulseaudio-default.pa │ ├── unity-autostart.desktop │ ├── unity-profiled │ ├── xbindkeysrc.scm │ ├── xiwi.conf │ ├── xorg-dummy.conf │ ├── xorg-intel-sna.conf │ ├── xserverrc │ ├── xserverrc-local.example │ ├── xserverrc-xiwi │ └── xserverrc-xorg ├── host-bin/ │ ├── crash_reporter_wrapper │ ├── edit-chroot │ ├── enter-chroot │ ├── mount-chroot │ ├── startcli │ ├── starte17 │ ├── startgnome │ ├── startkde │ ├── startkodi │ ├── startlxde │ ├── startunity │ ├── startxfce4 │ ├── startxiwi │ └── unmount-chroot ├── host-ext/ │ ├── .gitignore │ ├── crouton/ │ │ ├── background.html │ │ ├── background.js │ │ ├── first.html │ │ ├── kiwi.nmf │ │ ├── manifest.json │ │ ├── popup.html │ │ ├── popup.js │ │ ├── window.html │ │ └── window.js │ ├── gencrx.sh │ └── nacl_src/ │ ├── .gitignore │ ├── Makefile │ ├── keycode_converter.h │ └── kiwi.cc ├── installer/ │ ├── .gitattributes │ ├── crouton │ ├── debian/ │ │ ├── defaults │ │ └── releases │ ├── functions │ ├── kali/ │ │ ├── defaults │ │ └── releases │ ├── main.sh │ ├── prepare.sh │ └── ubuntu/ │ ├── ar │ ├── bootstrap │ ├── defaults │ ├── getrelease.sh │ ├── pkgdetails │ ├── prepare │ └── releases ├── src/ │ ├── fbserver-proto.h │ ├── fbserver.c │ ├── findnacld.c │ ├── freon.c │ ├── vtmonitor.c │ ├── websocket.c │ ├── websocket.h │ └── xi2event.c ├── targets/ │ ├── audio │ ├── chrome │ ├── chrome-beta │ ├── chrome-common │ ├── chrome-dev │ ├── chromium │ ├── cli-extra │ ├── common │ ├── core │ ├── e17 │ ├── extension │ ├── gnome │ ├── gnome-desktop │ ├── gtk-extra │ ├── kde │ ├── kde-desktop │ ├── keyboard │ ├── kodi │ ├── lxde │ ├── lxde-desktop │ ├── post-common │ ├── touch │ ├── unity │ ├── unity-desktop │ ├── x11 │ ├── x11-common │ ├── xbmc │ ├── xfce │ ├── xfce-desktop │ ├── xiwi │ └── xorg └── test/ ├── autotest_control.template ├── daemon.sh ├── genreport.sh ├── reports/ │ ├── 00-all.sh │ ├── 35-xorg.sh │ └── w0-common.sh ├── run.sh └── tests/ ├── 00-tester ├── 10-basic ├── 11-32-on-64 ├── 12-edit-chroot ├── 14-background ├── 15-media ├── 16-targetsfile ├── 17-encryption ├── 18-upgrade ├── 20-logind ├── 30-audio ├── 35-xorg ├── 37-xiwi ├── w0-common ├── w1-e17 ├── w2-gnome ├── w2d-gnome-desktop ├── w3-kde ├── w3d-kde-desktop ├── w4-lxde ├── w4d-lxde-desktop ├── w5-unity ├── w5d-unity-desktop ├── w6-kodi ├── w7-xfce ├── w7d-xfce-desktop └── x0-alltargets ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/CONTRIBUTING.md ================================================ ## For users who want to report an issue Please read [this wiki page] (https://github.com/dnschneid/crouton/wiki/Common-issues-and-reporting) for instructions on reporting an issue. ## For contributors Please read [this section](https://github.com/dnschneid/crouton#i-want-to-be-a-contributor) of the README and the following relevant sections first. ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ ``` Please paste the output of the following command here: sudo edit-chroot -all ``` #### Please describe your issue: #### If known, describe the steps to reproduce the issue: ================================================ FILE: .gitignore ================================================ *.tar.* .*.swp .*.tmp nacl_sdk/ /crouton* /test/run /releases/ ================================================ FILE: AUTHORS ================================================ Google, Inc ================================================ FILE: CONTRIBUTORS ================================================ Aaron Zauner Alex Bennée Alfred Suleymanov Alfriadox Alireza Ghasemi Andrew Kanner Aron Griffis Aurelien Lourot Avi Zajac Blaine Bublitz Braden Farmer brannon brmbrmcar Bryce Thorup Chris Galle Christopher Mårtensson Chris Varga Chuan Ji Corey Garst Daniel Haber David Reveman David Schneider Dennis Lockhart dimonf dumpweed Eugene Y. Q. Shen George Shank Icecream95 Igor Bukanov JackMacWindows Jake Waksbaum Javi Merino jessaustin Jim Tittsler Johan Lorentzon John Tantalo joshua stein Justin Frankel Justin Guy Kenny Strawn Kerwin Hui Lef Ioannidis Magnus Nyberg Masaki Muranaka Maurice van Kruchten Micah Lee Michael Mattioli Michael Moss Michael Orr Mike Kasick Mikito Takada Miles Whittaker Neal McBurnett Nevada Romsdahl Nicolas Boichat Pete Baldridge Phillip Pearson Rich Murphey Ricky Brent Ryan Fowler Samuel Dionne-Riel Simon Podhajsky Stephen Barber Steve Desmond Steven Maude Steven Merrill Ted Matsumura Tobbe Lundberg Tom Dunlap Tony Xue William Kong William Ransohoff William W. Wu Yang Wang Yu-Hsi Chiang Yuri Pole Yushin Washio zguithues Zopolis4 ================================================ FILE: LICENSE ================================================ // Copyright (c) 2016 The crouton Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: Makefile ================================================ # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. TARGET = crouton EXTTARGET = crouton.zip LIBS = src/freon.c LIBSTARGETS = $(patsubst src/%.c, crouton%.so, $(LIBS)) SRCTARGETS = $(patsubst src/%.c,crouton%,$(filter-out $(LIBS),$(wildcard src/*.c))) CONTRIBUTORS = CONTRIBUTORS WRAPPER = build/wrapper.sh SCRIPTS_NOSYM := \ $(wildcard host-bin/*) \ $(wildcard installer/*.sh) installer/functions \ $(wildcard installer/*/*) \ $(wildcard src/*) \ $(wildcard targets/*) SCRIPTS := \ $(wildcard chroot-bin/*) \ $(wildcard chroot-etc/*) EXTPEXE = host-ext/crouton/kiwi.pexe EXTPEXESOURCES = $(wildcard host-ext/nacl_src/*.h) \ $(wildcard host-ext/nacl_src/*.cc) EXTSOURCES = $(wildcard host-ext/crouton/*) BOOTSTRAPS := $(wildcard installer/*/bootstrap) BUILDDIR = crouton.build GENVERSION = build/genversion.sh CONTRIBUTORSSED = build/CONTRIBUTORS.sed RELEASE = build/release.sh VERSION = 1 TARPARAMS ?= -j CFLAGS=-g -Wall -Werror -Wno-error=unused-function -Os croutonfbserver_LIBS = -lX11 -lXdamage -lXext -lXfixes -lXtst croutonxi2event_LIBS = -lX11 -lXi croutonfreon.so_LIBS = -ldl -ldrm -I/usr/include/libdrm croutonwebsocket_DEPS = src/websocket.h croutonfbserver_DEPS = src/websocket.h ifeq ($(wildcard .git/HEAD),) GITHEAD := else GITHEADFILE := .git/refs/heads/$(shell cut -d/ -f3 '.git/HEAD') ifeq ($(wildcard $(GITHEADFILE)),) GITHEAD := .git/HEAD else GITHEAD := .git/HEAD .git/refs/heads/$(shell cut -d/ -f3 '.git/HEAD') endif endif $(TARGET): $(WRAPPER) $(BUILDDIR) $(GENVERSION) $(GITHEAD) Makefile { \ sed -e "s/\$$TARPARAMS/$(TARPARAMS)/" \ -e "s/VERSION=.*/VERSION='$(shell $(GENVERSION) $(VERSION))'/" \ $(WRAPPER) \ && (cd $(BUILDDIR) && tar --owner=root --group=root -c $(TARPARAMS) *)\ && chmod +x /dev/stdout \ ;} > $(TARGET) || ! rm -f $(TARGET) $(BUILDDIR): $(SCRIPTS) $(SCRIPTS_NOSYM) Makefile rm -rf $(BUILDDIR) && mkdir -p $(BUILDDIR) \ && cp -at $(BUILDDIR) --parents $(SCRIPTS) \ && cp -Lprt $(BUILDDIR) --parents $(SCRIPTS_NOSYM) \ && for bootstrap in $(BOOTSTRAPS); do \ tmp=$(BUILDDIR); \ [ -h "$$bootstrap" ] && continue; \ echo "Preparing bootstrap dependencies for $$bootstrap" >&2; \ tmp=$(BUILDDIR) sh -e "$$bootstrap" \ || ! rm -rf $(BUILDDIR) || exit 1; \ done $(EXTTARGET): $(EXTSOURCES) Makefile rm -f $(EXTTARGET) && zip -q --junk-paths $(EXTTARGET) $(EXTSOURCES) $(EXTPEXE): $(EXTPEXESOURCES) $(MAKE) -C host-ext/nacl_src $(SRCTARGETS): $(patsubst crouton%,src/%.c,$@) $($@_DEPS) Makefile gcc $(CFLAGS) $(patsubst crouton%,src/%.c,$@) $($@_LIBS) -o $@ $(LIBSTARGETS): $(patsubst crouton%.so,src/%.c,$@) $($@_DEPS) Makefile gcc $(CFLAGS) -shared -fPIC $(patsubst crouton%.so,src/%.c,$@) $($@_LIBS) -o $@ extension: $(EXTTARGET) $(CONTRIBUTORS): $(GITHEAD) $(CONTRIBUTORSSED) git shortlog -s | sed -f $(CONTRIBUTORSSED) | sort -uf > $(CONTRIBUTORS) contributors: $(CONTRIBUTORS) release: $(CONTRIBUTORS) $(TARGET) $(RELEASE) [ ! -d .git ] || git status | grep -q 'working [a-z]* clean' || \ { echo "There are uncommitted changes. Aborting!" 1>&2; exit 2; } $(RELEASE) $(TARGET) force-release: $(CONTRIBUTORS) $(TARGET) $(RELEASE) $(RELEASE) -f $(TARGET) all: $(TARGET) $(SRCTARGETS) $(LIBSTARGETS) $(EXTTARGET) clean: rm -f $(TARGET) $(EXTTARGET) $(SRCTARGETS) $(LIBSTARGETS) rm -rf $(BUILDDIR) .PHONY: all clean contributors extension release force-release ================================================ FILE: README.md ================================================ # crouton: Chromium OS Universal Chroot Environment crouton is a set of scripts that bundle up into an easy-to-use, Chromium OS-centric chroot generator. Ubuntu, Debian, and Kali are supported (using debootstrap behind the scenes), but "Chromium OS Debian and Ubuntu (plus one distro) EOL'd Chroot Environment" doesn't acronymize as well (crodupodece is admittedly pretty fun to say, though). ### 🪦 crouton is now end-of-life 🪦 All good things must come to an end, and considering * Chromium OS's introduction of increasingly strict shell safeguards, * the [change in cras's build tools](https://github.com/dnschneid/crouton/issues/4958), * the [removal of manifest v2 extensions](https://github.com/dnschneid/crouton/pull/5094), * the [removal of PNaCl, breaking xiwi](https://github.com/dnschneid/crouton/issues/5130), * oh, and Chromium OS being replaced by Android there's really not much to gain from continued development. Put another way, the proverbial mixed salad is just about out of tasty crunchy bits, and the remaining morsels have gone a bit stale. And for some reason someone's about to swap all the lettuce out for onion rings? Point is, it's time to stop mixing dressings. Unless it's ranch, I guess, since that goes with just about anything. But this is crouton, not some Ready-for-Android Native Chroot Host, alas. Anyway, this means that: * The repo is now locked, and no further changes will be considered. * Eventually someone will want the latest Ubuntu added to the release list. See [this commit](https://github.com/dnschneid/crouton/commit/6d80f57b91c39d10b29fde861aac5a2b5b9b3910) for an example of how to do it on your own copy. * Sometime around July 2025, the GitHub project will be archived, making the issue tracker, discussions, and wiki read-only. * For the safety of users and stability of crouton's functionality for those on EOL devices, offers to take over the dnschneid/crouton repo or extension will be declined, and requests to change link destinations will be rejected. If you have an EOL device, though, crouton is still a great match for you! * Chromium OS version 110 and earlier, everything should work! Breathe new life into your old devices!! * Starting 111, crouton can't compile cras, so audio devices cannot be shared with Chromium OS * Starting 117, sudo in crosh is disabled and you'll need to use VT-2 * Starting 133 (139 for enterprise devices), manifest v2 is disabled and you won't be able to run the extension (easy switching, clipboard sync, xiwi) * Also starting 133 (139 for enterprise devices), pnacl is disabled, so even if you somehow got the extension working, xiwi won't function * Beyond that, it's anybody's guess as to what will break ## Moving right along... :warning: **Steps to install crouton have changed!** :warning: Chromium OS has introduced several security features over the years that impede the installation and usage of crouton. If your device is no longer receiving updates, the steps below will likely work for you. However, if you are still having trouble, please try the [community-maintained instructions](https://github.com/dnschneid/crouton/wiki/Updated-Installation-Instructions-for-Crouton). In addition, goo.gl is going away! That means the goo.gl/fd3zc you know and love has been replaced with [git.io/JZEs0](https://git.io/JZEs0). That's a zero at the end, if you were wondering. Both just point to [github](https://raw.githubusercontent.com/dnschneid/crouton/master/installer/crouton), so you can always just memorize the full link instead, which (fun fact) does not include any numbers at all! ## "crouton"...an acronym? It stands for _ChRomium Os Universal chrooT envirONment_ ...or something like that. Do capitals really matter if caps-lock has been (mostly) banished, and the keycaps are all lower-case? Moving on... ## Who's this for? Anyone who wants to run straight Linux on their Chromium OS device, and doesn't care about physical security. You're also better off having some knowledge of Linux tools and the command line in case things go funny, but it's not strictly necessary. ## What's a chroot? Like virtualization, chroots provide the guest OS with their own, segregated file system to run in, allowing applications to run in a different binary environment from the host OS. Unlike virtualization, you are *not* booting a second OS; instead, the guest OS is running using the Chromium OS system. The benefit to this is that there is zero speed penalty since everything is run natively, and you aren't wasting RAM to boot two OSes at the same time. The downside is that you must be running the correct chroot for your hardware, the software must be compatible with Chromium OS's kernel, and machine resources are inextricably tied between the host Chromium OS and the guest OS. What this means is that while the chroot cannot directly access files outside of its view, it *can* access all of your hardware devices, including the entire contents of memory. A root exploit in your guest OS will essentially have unfettered access to the rest of Chromium OS. ...but hey, you can run [TuxRacer](https://en.wikipedia.org/wiki/Tux_Racer)! ### What about dem crostinis though? [Crostini](https://chromium.googlesource.com/chromiumos/docs/+/HEAD/containers_and_vms.md) is an official project within Chromium OS to bring the Linux shell and apps to the platform *in verified mode* with clean integration, multi-layered security, and all the polish you expect from Chromium OS proper. That means compared to crouton, Crostini has official support, competent engineers, and code that looks a little less like ramen. crouton, in its defense, has wider device compatibility, enables direct hardware access, and is named after an objectively tastier bread-based food item. There's a solid community on [Reddit](https://www.reddit.com/r/Crostini/) if you'd like to try Crostini out. If it works for you -- great! No hard feelings. If in the end you decide that crouton suits you better, read on! Note: you can't get the best of both worlds by installing crouton inside of Crostini. The technology (and life itself) just doesn't work that way. Not to mention a crouton Crostini would look ridiculous and be impossible to eat without getting bits everywhere. ## Prerequisites You need a device running Chromium OS that has been switched to developer mode. For instructions on how to do that, go to [this Chromium OS wiki page](https://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices), click on your device model and follow the steps in the *Entering Developer Mode* section. Note that developer mode, in its default configuration, is *completely insecure*, so don't expect a password in your chroot to keep anyone from your data. crouton does support encrypting chroots, but the encryption is only as strong as the quality of your passphrase. Consider this your warning. It's also highly recommended that you install the [crouton extension](https://chromewebstore.google.com/detail/crouton-integration/gcpneefbbnfalgjniomfjknbcgkbijom), which, when combined with the `extension` or `xiwi` targets, provides much improved integration with Chromium OS. That's it! Surprised? ## Usage crouton is a powerful tool, and there are a *lot* of features, but basic usage is as simple as possible by design. If you're just here to use crouton, you can grab the latest release from [https://git.io/JZEs0](https://git.io/JZEs0). Download it, pop open a shell (Ctrl+Alt+T, type `shell` and hit enter), make the installer executable with `sudo install -Dt /usr/local/bin -m 755 ~/Downloads/crouton`, then launch it with `sudo crouton` to see the help text. See the "examples" section for some usage examples. If you're modifying crouton, you'll probably want to clone or download the repo into a subdirectory of `/usr/local` and then either run `installer/main.sh` directly, or use `make` to build your very own `crouton`. You can also download the latest release, install it as above and run `crouton -x` to extract out the juicy scripts contained within, but you'll be missing build-time stuff like the Makefile. You also need to remember to place the unbundled scripts somewhere in `/usr/local` in order to be able to execute them. crouton uses the concept of "targets" to decide what to install. While you will have apt-get in your chroot, some targets may need minor hacks to avoid issues when running in the chrooted environment. As such, if you expect to want something that is fulfilled by a target, install that target when you make the chroot and you'll have an easier time. Don't worry if you forget to include a target; you can always update the chroot later and add it. You can see the list of available targets by running `crouton -t help`. Once you've set up your chroot, you can easily enter it using the newly-installed `enter-chroot` command, or one of the target-specific start\* commands. Ta-da! That was easy. ## Examples ### The easy way (assuming you want an Ubuntu LTS with Xfce) 1. Download `crouton` 2. Open a shell (Ctrl+Alt+T, type `shell` and hit enter) 3. Copy the installer to an executable location by running `sudo install -Dt /usr/local/bin -m 755 ~/Downloads/crouton` 4. Now that it's executable, run the installer itself: `sudo crouton -t xfce` 5. Wait patiently and answer the prompts like a good person. 6. Done! You can jump straight to your Xfce session by running `sudo enter-chroot startxfce4` or, as a special shortcut, `sudo startxfce4` 7. Cycle through Chromium OS and your running graphical chroots using Ctrl+Alt+Shift+Back and Ctrl+Alt+Shift+Forward. 8. Exit the chroot by logging out of Xfce. ### With encryption! 1. Add the `-e` parameter when you run crouton to create an encrypted chroot or encrypt a non-encrypted chroot. 2. You can get some extra protection on your chroot by storing the decryption key separately from the place the chroot is stored. Use the `-k` parameter to specify a file or directory to store the keys in (such as a USB drive or SD card) when you create the chroot. Beware that if you lose this file, your chroot will not be decryptable. That's kind of the point, of course. ### Hey now, Ubuntu 16.04 is pretty old; I'm young and hip 1. The `-r` parameter specifies which distro release you want to use. 2. Run `crouton -r list` to list the recognized releases and which distros they belong to. ### Wasteful redundancies are wasteful: one clipboard, one browser, one window 1. Install the [crouton extension](https://chromewebstore.google.com/detail/crouton-integration/gcpneefbbnfalgjniomfjknbcgkbijom) into Chromium OS. 2. Add the `extension` or `xiwi` version to your chroot. 3. Try some copy-pasta, or uninstall all your web browsers from the chroot. *Installing the extension and its target gives you synchronized clipboards, the option of using Chromium OS to handle URLs, and allows chroots to create graphical sessions as Chromium OS windows.* ### I don't always use Linux, but when I do, I use CLI 1. You can save a chunk of space by ditching X and just installing command-line tools using `-t core` or `-t cli-extra` 2. Enter the chroot in as many crosh shells as you want simultaneously using `sudo enter-chroot` 3. Use the [Crosh Window](https://chromewebstore.google.com/detail/crosh-window/nhbmpbdladcchdhkemlojfjdknjadhmh) extension to keep Chromium OS from eating standard keyboard shortcuts. 4. If you installed cli-extra, `startcli` will launch a new VT right into the chroot. ### A new version of crouton came out; my chroot is therefore obsolete and sad 1. Exit the chroot if you have it open. 2. If you haven't already, download `crouton`, and copy it so it works: `sudo install -Dt /usr/local/bin -m 755 ~/Downloads/crouton` 3. Update your chroot with `sudo crouton -u -n chrootname`. It will update all installed targets. ### I want to open my desktop in a window or a tab but I don't have the 'xiwi' target/xmethod. 1. Add 'xiwi' or any other target to an existing chroot with the `-u` option: `sudo crouton -t xiwi -u -n chrootname` This will also make 'xiwi' the default xmethod. 2. If you want to keep the 'xorg' xmethod as the default then pick it first: `sudo sh crouton -t xorg,xiwi -u -n chrootname` ### A backup a day keeps the price-gouging data restoration services away 1. `sudo edit-chroot -b chrootname` backs up your chroot to a timestamped tarball in the current directory. Chroots are named either via the `-n` parameter when created or by the release name if -n was not specified. 2. `sudo edit-chroot -r chrootname` restores the chroot from the most recent timestamped tarball. You can explicitly specify the tarball with `-f` 3. If your machine is new, powerwashed, or held upside-down and shaken, you can use the crouton installer to restore a chroot and relevant scripts: `sudo crouton -f mybackup.tar.gz` *Unlike with Chromium OS, the data in your chroot isn't synced to the cloud.* ### This chroot's name/location/password/existence sucks. How to fix? 1. Check out the `edit-chroot` command; it likely does what you need it to do. 2. If you set a Chromium OS root password, you can change it with `sudo chromeos-setdevpasswd` 3. You can change the password inside your chroot with `passwd` ### I want to install the chroot to another location 1. Use `-p` to specify the directory in which to install the chroot and scripts. Be sure to quote or escape spaces. 2. When entering the chroot for the first time each boot, you will first need to ensure the place you've installed the scripts is in a place that allows executables to run. Determine the mountpoint by running `df --output=target /path/to/enter-chroot`, then mark the mount exec with `sudo mount -o remount,exec /path/to/mountpoint`. 3. You can then launch the chroot by specifying the full path of any of the enter-chroot or start* scripts (i.e. `sudo /path/to/enter-chroot`), or use the `-c` parameter to explicitly specify the chroots directory. *If for some reason you have to run the installer without touching the local disk, you can (for the time being) run `curl -fL https://git.io/JZEs0 | sudo sh -s -- options_for_crouton_installer`. Note that this will definitely break in the near future, so don't depend on it.* ### Downloading bootstrap files over and over again is a waste of time 1. Download `crouton` 2. Open a shell (Ctrl+Alt+T, type `shell` and hit enter) 3. Copy the installer to an executable location by running `sudo install -Dt /usr/local/bin -m 755 ~/Downloads/crouton` 4. Now that it's executable, use the installer to build a bootstrap tarball: `sudo crouton -d -f ~/Downloads/mybootstrap.tar.bz2` 5. Include the `-r` parameter if you want to specify for which release to prepare a bootstrap. 6. You can then create chroots using the tarball by running `sudo crouton -f ~/Downloads/mybootstrap.tar.bz2`. Make sure you also specify the target environment with `-t`. *This is the quickest way to create multiple chroots at once, since you won't have to determine and download the bootstrap files every time.* ### Targets are cool. Abusing them for fun and profit is even cooler 1. You can make your own target files (start by copying one of the existing ones) and then use them with any version of crouton via the `-T` parameter. *This is great for automating common tasks when creating chroots.* ### Help! I've created a monster that must be slain! 1. The delete-chroot command is your sword, shield, and only true friend. `sudo delete-chroot evilchroot` 2. It's actually just a shortcut to `sudo edit-chroot -d evilchroot`, which I suppose makes it a bit of a deceptive Swiss Army knife friend...still good? ## Tips * Chroots are cheap! Create multiple ones using `-n`, break them, then make new, better ones! * You can change the distro mirror from the default by using `-m` * Want to use a proxy? `-P` lets you specify one (or disable it). * A script is installed in your chroot called `brightness`. You can assign this to keyboard shortcuts to adjust the brightness of the screen (e.g. `brightness up`) or keyboard (e.g. `brightness k down`). * Multiple monitors will work fine in the chroot, but you may have to switch to Chromium OS and back to enable them. * You can make commands run in the background so that you can close the terminal. This is particularly useful for desktop environments: try running `sudo startxfce4 -b` * Want to disable Chromium OS's power management? Run `croutonpowerd -i` * Only want power management disabled for the duration of a command? `croutonpowerd -i command and arguments` will automatically stop inhibiting power management when the command exits. * Have a Pixel or two or 4.352 million? `-t touch` improves touch support. * Want to share some files and/or folders between Chromium OS and your chroot? Check out the `/etc/crouton/shares` file, or read all about it in the wiki. * Want more tips? Check the [wiki](https://github.com/dnschneid/crouton/wiki). ## Issues? Running another OS in a chroot is a pretty messy technique (although it's hidden behind very pretty scripts), and while these scripts are relatively mature, Chromium OS is changing all the time so problems are not surprising. Check the issue tracker and file a bug if your issue isn't there. When filing a new bug, include the output of `croutonversion` run from inside the chroot or, if you cannot mount your chroot, include the output of `cat /etc/lsb-release` from Crosh. ## I want to be a Contributor! That's great! But before your code can be merged, you'll need to have signed the [Individual Contributor License Agreement](https://cla.developers.google.com/clas/new?kind=KIND_INDIVIDUAL&domain=DOMAIN_GOOGLE). Don't worry, it only takes a minute and you'll definitely get to keep your firstborn, probably. If you've already signed it for contributing to Chromium or Chromium OS, you're already done. If you don't know what to do with your time as an official Contributor, keep in mind that crouton is maintenance-only and will only be accepting a limited amount of changes. That having been said, here's some suggestions: * Really like a certain desktop environment? Fork crouton, add the target, and let people know in the discussions area. * Is your distro underrepresented? Want to contribute to the elusive and mythical beast known as "croagh"? Fork crouton, add the distro, and people will come. * Discovered a bug lurking within the scripts, or a papercut that bothers you just enough to make you want to actually do something about it? You guessed it: fork crouton, fix everything, and create a pull request. * Are most bugs too high-level for you to defeat? Grind up some [EXP](https://en.wikipedia.org/wiki/Experience_point) by using your fork to eat [pie](https://github.com/dnschneid/crouton/labels/pie). ## Are there other, non-Contributory ways I can help? Yes! ## But how? There's a way For Everyone to help! * Something broken? File a bug! Bonus points if you try to fix it. It helps if you provide the output of `croutonversion` (or the output of `cat /etc/lsb-release` from Crosh) when you submit the bug. * Look through [open issues](https://github.com/dnschneid/crouton/issues?state=open) and see if there's a topic or application you happen to have experience with. And then, preferably, share that experience with others. * Find issues that need [wiki entries](https://github.com/dnschneid/crouton/issues?labels=needswiki&state=open,closed) and add the relevant info to the [wiki](https://github.com/dnschneid/crouton/wiki). Or just add things to/improve things in the wiki in general, but do try to keep it relevant and organized. * Really like a certain desktop environment, but not up for coding? Open or comment on a bug with steps to get things working well. ## License crouton (including this eloquently-written README) is copyright © 2016 The crouton Authors. All rights reserved. Use of the source code included here is governed by a BSD-style license that can be found in the LICENSE file in the source tree. ================================================ FILE: README.zh.md ================================================ # Crouton中文版教程 > 基于crouton项目README的英文版进行汉化,部分内容没有进行汉化,建议有能力者优先阅读英文版本。 ## 简介 ​ Chroot是Chromium OS Universal Chroot Environment 的简写,是一系列脚本的合集,利用Linux的Chroot,在Chromebook上同时运行Chrome OS和某个Linux发行版。 ## Chroot介绍 ​ Chroot命令用来在指定的根目录下运行指令。Chroot的这种功能可以为第二系统提供一个隔离的文件系统,就像虚拟化一样,但是第二系统实际上仍然在主系统的文件系统下面工作,在进程和网络层面,chroot并没有进行隔离。 ​ 至于详细的内容,为什么不去问问[百度搜索](https://www.baidu.com/s?wd=chroot)? ## 进入正题 ### 环境 - **良好的**网络环境 - 进入**开发者模式**的Chromebook,相关操作请进入[这个页面](https://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices)(英文页面),点击对应的设备型号,按照*Entering Developer Mode*章节的步骤进行 - 强烈建议安装[Crouton插件](https://chromewebstore.google.com/detail/crouton-integration/gcpneefbbnfalgjniomfjknbcgkbijom),配合`extension`或者`xiwi`目标,可以提高第二系统与Chrome OS之间的交互体验 ### 用法 1. 你需要从[这里](https://git.io/JZEs0)下载Crouton脚本。~~什么?下不下来?关我什么事~~ 2. 然后打开shell(`ctrl+alt+T`,在打开的窗口中输入`shell`,然后回车)。 3. 输入`sudo install -Dt /usr/local/bin -m 755 ~/Downloads/crouton`,这一步是将下载下来的脚本安装到`/usr/local/bin`这个可执行目录里面。 4. `sudo crouton`可以查看帮助,本教程**示例**部分会有一些命令使用举例。 如果你想对Crouton稍作修改,可以将本项目下载到`/usr/local`,直接运行`installer/main.sh`或者使用`make`进行编译。你也可以按上述四步安装crouton后,使用`crouton -x`将包含的脚本解压,不过那样的话你需要自己编写编译所需的文件,以及记住脚本所在的位置。 Crouton使用“目标”('targets')来决定安装什么。可用的目标可以运行`crouton -t help`来查看。 安装之后,可以输入`enter-chroot`,或者由你选择的安装目标所决定的 start* 命令。具体的命令,安装完成后终端会有介绍(英文)。 ## 示例 **简单示例(安装Ubuntu LTS,使用Xfce桌面环境)** 1. 下载Crouton 2. 打开shell(`ctrl+alt+T`,在打开的窗口中输入`shell`,然后回车)。 3. 输入`sudo install -Dt /usr/local/bin -m 755 ~/Downloads/crouton` 4. `sudo crouton -t xfce` 5. 等吧,可以喝杯星巴克 6. 安装完成后,使用`sudo enter-chroot startxfce4`,或者`sudo startxfce4`运行chroot,会自动跳至Xfce 7. 登出/注销(logout)Xfce来退出chroot,**在Xfce里点击关机是没有用的**。 **加密** 1. 运行crouton是可以添加`-e`参数来创建一个加密的chroot环境,或者加密一个未加密的chroot环境 2. 使用`-k`参数来指定储存密钥的路径 **想用别的系统?** 1. `-r`参数可以指定你想要使用的发行版和版本代号 2. `crouton -r list`可以查看支持的发行版和版本代号(英文) **说好的“更好的交互体验”?** 1. 在Chrome OS安装[Crouton插件](https://chromewebstore.google.com/detail/crouton-integration/gcpneefbbnfalgjniomfjknbcgkbijom) 2. 在chroot环境中添加`extension`或者`xiwi`目标 这样可以同步chroot环境和主系统的剪贴板,允许chroot环境的程序在Chrome OS界面中窗口化运行。 **只使用命令行** 1. 指定安装目标时可以只使用`-t core`或者`-t cli-extra` 2. 使用`sudo enter-chroot`进入chroot环境 3. 使用[Crosh Window插件](https://chromewebstore.google.com/detail/crosh-window/nhbmpbdladcchdhkemlojfjdknjadhmh),防止chroot命令行环境导致的快捷键失效 **升级chroot环境** ​ 使用`sudo crouton -u -n chrootname`来升级chroot环境中的系统。 **安装后想添加一些安装目标?** ​ 使用`-u`参数来添加安装目标。 ​ 比如,添加`xiwi`目标:`sudo crouton -t xiwi -u -n chrootname` ​ 上述命令会让xiwi成为默认的[X窗口方法](https://baike.baidu.com/item/X%E7%AA%97%E5%8F%A3/1471357?fr=aladdin)(原默认方法为xorg),如果想让X窗口方法继续保持默认: ​ `sudo crouton -t xorg,xiwi -u -n chrootname` **备份** ​ `sudo edit-chroot -b chrootname`会在命令运行目录下生成chroot环境的tar格式的备份文件(带备份时间戳),Chroot环境的名字可以在安装时由`-n`参数指定,未指定时默认为所安装的Linux发行版版本代号(例如,默认的Ubuntu 16.04LTS版本代号为xenial) ​ `sudo edit-chroot -r chrootname`默认恢复最近一次的备份文件。可以用`-r`参数指定恢复文件 ​ 对全新或者重置过的电脑,可以使用Crouton的恢复命令:`sudo crouton -f mybackup.tar.gz` **更改安装位置** ​ `-p`参数可指定chroot的安装位置。 ​ 每次启动电脑后第一次启动chroot,请确定chroot的安装位置是可执行(executable)的: 1. 确定挂载点:`df --output=target /path/to/enterchroot` 2. 使挂载点可读写:`sudo mount -o remount,exec /path/to/mountpoint` **删除Chroot环境** ​ `sudo delete-chroot chrootname` ## 使用提醒 - 使用`-n`来指定Chroot环境的名字,可以创建多个chroot环境 - 使用`-m`参数更改镜像源 - `-P`参数开启/关闭Chroot环境的代理,仅支持http/https - chroot内置`brightness`脚本,可以: - 调节屏幕亮度(比如,在chroot内运行`brightness up`) - 调节背光键盘亮度(比如,在chroot内运行:`brightness k down`) - 使用多屏可能需要先切换到Chrome OS界面,然后再切换回来 - 运行命令添加`-b`参数可以让chroot在后台运行,比如:`sudo startxfce4 -b` - `croutonpowerd -i`可以关闭Chrome OS的电源管理 - `croutonpowerd -i command and arguments`可以指定在chroot执行某些命令时关闭Chrome OS的电源管理 - `touch`安装目标可以改善触摸屏设备的使用体验 - 本项目Wiki中有关于文件共享的介绍 - [Wiki](https://github.com/dnschneid/crouton/wiki)中也有更多其他提示(英文) ================================================ FILE: build/CONTRIBUTORS.sed ================================================ s/^[ \t0-9]*// s/^DennisL.*$/Dennis Lockhart/ s/^drinkcat$/Nicolas Boichat/ s/^divx118$/Maurice van Kruchten/ s/^haberda$/Daniel Haber/ s/^tedm$/Ted Matsumura/ s/^magnus$/Magnus Nyberg/ s/^eyqs$/Eugene Y. Q. Shen/ /^nromsdahl$/d /^root$/d /^ttk153$/d /^lnxsrt$/d /\?/d ================================================ FILE: build/genversion.sh ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Outputs a version string with the specified prefix. if [ "$#" != 1 ]; then echo "Usage: ${0##*/} VERSION" 1>&2 exit 1 fi source='' # Get the branch from git git="`dirname "$0"`/../.git" if [ -f "$git/HEAD" ]; then source="`cut -d/ -f3 "$git/HEAD"`" if [ -n "$source" ]; then if [ -f "$git/refs/heads/$source" ]; then source="$source:`head -c 8 "$git/refs/heads/$source"`" else source="${source%"${source#????????}"}" fi source="~$source" fi fi VERSION="$1-%Y%m%d%H%M%S$source" exec date "+$VERSION" ================================================ FILE: build/release.sh ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e # Generates a release in the releases/ directory (which should be a checkout of # the releases branch of the same repo) and pushes it. USAGE="Usage: ${0##*/} [-f] bundle [..] -f Hard-reset the releases branch in case of unpushed releases." FORCE='' # CD into the repo's root directory dir="`readlink -f -- "$0"`" cd "${dir%/*}/.." # Import common functions . installer/functions if [ "$1" = '-f' ]; then FORCE=y shift fi if [ "$#" = 0 ]; then error 2 "$USAGE" fi # Check the releases directory and update, or create it if necessary if [ -d releases/.git ]; then if ! grep -q 'releases$' releases/.git/HEAD; then error 1 'releases/ is not in the releases branch.' fi if ! awk "/'releases'/{print \$1}" releases/.git/FETCH_HEAD | \ diff -q releases/.git/refs/heads/releases - >/dev/null; then echo 'releases/ is ahead of or not up-to-date with remote' 1>&2 if [ -z "$FORCE" ]; then exit 1 fi fi git -C releases fetch origin releases git -C releases reset --hard origin/releases elif [ -e releases ]; then error 1 "releases/ is not a git repo" else url="`git remote -v | awk '$1=="origin" && $3=="(fetch)" {print $2}'`" git clone --single-branch --branch releases --reference . "$url" releases fi # Apply the releases for bundle in "$@"; do bundle="${bundle##*/}" if [ ! -f "$bundle" ]; then error 1 "$bundle bundle does not exist" fi version="`sh "$bundle" -V`" if [ "$version" = "${version#crouton*:}" ]; then error 1 "$bundle bundle is invalid" fi branch="${version#*~}" branch="${branch%:*}" dest="${branch#master}" dest="crouton${dest:+-}$dest" # Compare the current release to avoid duplicates if [ -f "releases/$dest" ] && \ sh "releases/$dest" -V | grep -q "${version#*~}"; then echo "Release already current: `sh "$bundle" -V`" continue fi # Copy it in and make a commit cp -fv "$bundle" "releases/$dest" git -C releases add "$dest" git -C releases commit -m "$version" done # Push the resulting releases git -C releases push origin releases git -C releases fetch origin releases exit 0 ================================================ FILE: build/wrapper.sh ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file of the source repository, which has been replicated # below for convenience of distribution: # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following disclaimer # in the documentation and/or other materials provided with the # distribution. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # This is a wrapped tarball. This script untars itself to a temporary directory # and then runs installer/main.sh with the parameters passed to it. # You can pass -x [directory] to extract the contents somewhere. set -e VERSION='git' # Minimum Chromium OS version is R45 stable CROS_MIN_VERS=7262 if [ "$1" = '-x' -a "$#" -le 2 ]; then # Extract to the specified directory. SCRIPTDIR="${2:-"${0##*/}.unbundled"}" mkdir -p "$SCRIPTDIR" else # Make a temporary directory and auto-remove it when the script ends. SCRIPTDIR='/tmp' # If we're running from a directory in /tmp, make the temporary directory a # subdirectory of it. if [ "${0#/tmp/}" != "$0" ]; then SCRIPTDIR="${0%/*}" fi SCRIPTDIR="`mktemp -d --tmpdir="$SCRIPTDIR" "${0##*/}.XXX"`" TRAP="rm -rf --one-file-system '$SCRIPTDIR';$TRAP" trap "$TRAP" INT HUP 0 fi # Extract this file after the ### line # TARPARAMS will be set by the Makefile to match the compression method. line="`awk '/^###/ { print FNR+1; exit 0; }' "$0"`" tail -n "+$line" "$0" | tar -x $TARPARAMS -C "$SCRIPTDIR" # Exit here if we're just extracting if [ -z "$TRAP" ]; then exit fi # See if we want to just run a script from the bundle if [ "$1" = '-X' ]; then script="$SCRIPTDIR/$2" if [ ! -f "$script" ]; then cd "$SCRIPTDIR" echo "USAGE: ${0##*/} -X DIR/SCRIPT [ARGS] Runs a script directly from the bundle. Valid DIR/SCRIPT combos:" 1>&2 ls chroot-bin/* host-bin/* 1>&2 if [ -n "$2" ]; then echo 1>&2 echo "Invalid script '$2'" 1>&2 fi exit 2 fi shift 2 # If this script was called with '-x' or '-v', pass that on SETOPTIONS="-e" if set -o | grep -q '^xtrace.*on$'; then SETOPTIONS="$SETOPTIONS -x" fi if set -o | grep -q '^verbose.*on$'; then SETOPTIONS="$SETOPTIONS -v" fi sh $SETOPTIONS "$script" "$@" exit "$?" fi # Execute the main script inline. It will use SCRIPTDIR to find what it needs. . "$SCRIPTDIR/installer/main.sh" exit ### end of script; tarball follows ================================================ FILE: chroot-bin/brightness ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # A shortcut script to control device brightness. # Usage: # brightness [b|k] [up|down|# [instant]] # Choose the device device='Screen' nokbd='' case "$1" in k*) device='Keyboard' nokbd='echo "Command not supported with keyboard backlight." 1>&2; exit 1' shift;; b*) shift;; esac # Handle user command print='' postcmd='' case "$1" in u*) precmd='Increase';; d*) precmd='Decrease';; [0-9]*) eval $nokbd; precmd='Set'; postcmd="Percent double:$1 int32:${2:-"1"}${2:+"2"}";; *) eval $nokbd; precmd='Get'; postcmd='Percent'; print='--print-reply';; esac host-dbus dbus-send --system --dest=org.chromium.PowerManager \ --type=method_call $print /org/chromium/PowerManager \ org.chromium.PowerManager.${precmd}${device}Brightness${postcmd} | { read -r junk read -r double percent if [ -n "$print" ]; then echo "${percent%.*}" fi } exit 0 ================================================ FILE: chroot-bin/crouton-noroot ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Wrapper for scripts that shouldn't be launched as root. # Symlink to launch the application with the same name in /usr/bin/ # Launch directly with a parameter to launch that executable # Launch directly with a blank or - first parameter and a word to just check and # exit with an exit code, printing out the second parameter as the app name. APPLICATION="${0##*/}" if [ "$APPLICATION" = 'crouton-noroot' ]; then if [ -z "$1" -o "$1" = '-' -o "$1" = '--' ]; then APPLICATION='' else APPLICATION="$1" shift fi else APPLICATION="/usr/bin/$APPLICATION" fi if [ "$USER" = root -o "$UID" = 0 ]; then app="${APPLICATION:-"$2"}" echo "Do not launch ${app##*/} as root inside the chroot." 1>&2 exit 2 elif [ -z "$APPLICATION" ]; then exit 0 fi exec "$APPLICATION" "$@" ================================================ FILE: chroot-bin/crouton-unity-autostart ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # Helper script for launching services normally managed by upstart. # Called during session's XDG autostart. RELEASE="`/usr/local/bin/croutonversion -r`" # Run the exec line from an Upstart conf file. Right now this assumes no space # in the path and no arguments. execfromconf() { local exec_cmd="`awk '/^exec/{print $2; exit}' "$1"`" if [ -x "$exec_cmd" ]; then "$exec_cmd" & fi } # Upstart launches unity on utopic+, and this needs to be started before # any dependent services. if [ "$RELEASE" = 'xenial' ]; then PATH="$PATH":/sbin /usr/bin/unity & fi # Launch window-stack-bridge for Unity HUD support, and unity-panel-service for # indicators. if [ "$RELEASE" != 'precise' ]; then services="unity-panel-service window-stack-bridge" for service in $services; do conf_file=/usr/share/upstart/sessions/"$service".conf execfromconf "$conf_file" done fi # If on trusty or later, indicators also need to be started. if [ "$RELEASE" = 'trusty' -o "$RELEASE" = 'xenial' ]; then for conf_file in /usr/share/upstart/sessions/*.conf; do if grep -q '^start on.* indicator-services-start' "$conf_file"; then execfromconf "$conf_file" fi done fi ================================================ FILE: chroot-bin/croutonclip ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # Synchronizes clipboard between X displays, making use of crouton's WebSocket # server and Chromium extension to synchronize the clipboard with Chromium OS VERBOSE='' . "`dirname "$0"`/../installer/functions" # rundisplay :X cmd ... # Run a command on the specified display rundisplay() { local disp="$1" shift DISPLAY="$disp" "$@" } copyclip() { next="$1" # Do not copy if next is empty (display cannot be detected), or # if current == next. Also set current=$next if $current is empty if [ -z "$next" -o "${current:="$next"}" = "$next" ]; then if [ -n "$VERBOSE" ]; then echo "==Current: $current==Next: $next==" 1>&2 fi return 0 fi if [ -n "$VERBOSE" ]; then echo ">>Current: $current>>" 1>&2 fi # Copy clipboard content from the current display { if [ "$current" = 'cros' ]; then echo -n 'R' | websocketcommand else # Check if display is still running if rundisplay "$current" xdpyinfo >/dev/null 2>&1; then echo -n 'R' rundisplay "$current" xsel -ob else echo -n "EUnable to open display '$current'." fi fi } | ( STATUS="`head -c 1`" if [ "$STATUS" != 'R' ]; then echo -n "croutonwebsocket error: " >&2 cat >&2 # Stop here (the clipboard content is lost in this case) exit 0 fi # Paste clipboard content to the next display if [ "$next" = 'cros' ]; then STATUS="`(echo -n 'W'; cat) | websocketcommand`" if [ "$STATUS" != 'WOK' ]; then # Write failed, skip Chromium OS (do not update $current) echo -n "croutonwebsocket error: $STATUS" >&2 exit 1 fi else # Do not override content if it "looks" the same # (we might have rich text or other content in the clipboard) cliptmp="`mktemp "croutonclip.XXX" --tmpdir=/tmp`" trap "rm -f '$cliptmp'" 0 cat > $cliptmp if ! rundisplay "$next" xsel -ob \ | diff -q - "$cliptmp" > /dev/null; then rundisplay "$next" xsel -ib < "$cliptmp" fi fi ) && current="$next" if [ -n "$VERBOSE" ]; then echo "<&2 fi } # Wait for the websocket server to get connected to the extension # Timeout after 10 seconds (twice crouton extension retry period) waitwebsocket() { timeout=10 while [ $timeout -gt 0 ]; do if [ -n "$VERBOSE" ]; then echo "Ping..." 1>&2 fi # Prepare and send a ping message MSG="PING$$$timeout" STATUS="`echo -n "$MSG" | websocketcommand`" if [ "$STATUS" = "$MSG" ]; then if [ -n "$VERBOSE" ]; then echo "OK!" 1>&2 fi return 0 fi if [ -n "$VERBOSE" ]; then echo "$STATUS" 1>&2 fi sleep 1 timeout=$(($timeout-1)) done echo "Timeout waiting for extension to connect." >&2 } # Assume current display is Chromium OS: avoid race as we may not be able to # detect the first VT/window change. current='cros' mkdir -m 775 -p "$CROUTONLOCKDIR" exec 3>>"$CROUTONLOCKDIR/clip" chmod -Rf g+rwX "$CROUTONLOCKDIR" || true chgrp -Rf crouton "$CROUTONLOCKDIR" || true if ! flock -n 3; then echo "Another instance of croutonclip running, waiting..." flock 3 fi addtrap "echo -n > '$CROUTONLOCKDIR/clip' 2>/dev/null" ( # This subshell handles USR1 signals from croutoncycle. # It prints a line when it receives a signal, or on VT change (we are able # to filter out duplicate notifications). # Start croutonwebsocket here to give "wait" something to wait for croutonwebsocket & addtrap "kill $! 2>/dev/null" waitwebsocket # Update on VT change (if user types Ctrl-Alt instead of Ctrl-Alt-Shift) if hash croutonvtmonitor 2>/dev/null; then croutonvtmonitor & addtrap "kill $! 2>/dev/null" fi trap "echo 'USR1'" USR1 # Set the PID of this subshell after the trap is in place sh -c 'echo -n "$PPID"' > "$CROUTONLOCKDIR/clip" # Force an update when started. echo "Force" # Wait until all the children have terminated (gets interrupted on signal, # which gives handlers a chance to run) while ! wait; do : done ) | ( # Do not hold the lock in this subshell and children (especially xsel) exec 3>/dev/null while read -r line; do display="`croutoncycle display`" copyclip "$display" done ) exit 1 ================================================ FILE: chroot-bin/croutoncycle ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. . "$(dirname "$0")/../installer/functions" USAGE="${0##*/} next|prev|cros|list|# Cycles through running graphical chroots. next: switch to the next display prev: switch to the previous display cros: switch directly to Chromium OS list: list all of the active displays and the associated chroot name #: switch to the nth item in the list, zero-indexed :#: switch directly to the chroot owning the specified display number" # Undocumented: # display: return display associated with current window # (used from croutonclip): # - cros: Chromium OS # - :0: Chromium OS X11 display (non-aura window) # - :1-9: chroot displays # s: informs of a Chromium OS window change (called from extension): # - cros: any Chromium OS window # - :1-9: kiwi window: X11 display number # force [command]: Force switching display, even if it does not appear # to be necessary. force='' if [ "${1#[Ff]}" != "$1" ]; then force='y' shift fi case "$1" in [Ll]*) cmd='l';; [Dd]*) cmd='d';; [Cc]*) cmd='0';; [Pp]*) cmd='p';; [Nn]*) cmd='n';; [Ss]*) cmd='s' disp="${1#s}";; :*) cmd="${1%%.*}"; cmd=":$((${cmd#:}))";; [0-9]*) cmd="$(($1))";; *) error 2 "$USAGE";; esac # Returns the chroot name of an X11 display specified in $1 on stdout getname() { local name='Unknown' if [ "$1" = 'cros' ]; then if [ -r '/var/host/lsb-release' ]; then name="$(awk -F= '/_RELEASE_NAME=/{print $2}' \ '/var/host/lsb-release')" fi else local crname="$(DISPLAY=":${1#:}" xprop -root CROUTON_NAME 2>/dev/null)" if [ "${crname%\"}" != "$crname" ]; then crname="${crname%\"}" name="${crname#*\"}" fi fi echo "$name" } # Only let one instance run at a time to avoid nasty race conditions # Make sure to release this before running websocket commands mkdir -m 775 -p "$CROUTONLOCKDIR" exec 3>"$CROUTONLOCKDIR/cycle" chmod -Rf g+rwX "$CROUTONLOCKDIR" || true chgrp -Rf crouton "$CROUTONLOCKDIR" || true flock 3 # set display command from extension if [ "$cmd" = 's' ]; then echo "$disp" > "$CRIATDISPLAY" if [ -s "$CROUTONLOCKDIR/clip" ]; then kill -USR1 "$(cat "$CROUTONLOCKDIR/clip")" || true fi exit 0 fi # Ensure environment sanity export XAUTHORITY='' # Set to y if there is any xiwi instance running xiwiactive='' # Prepare display list for easier looping displist='cros' for disp in /tmp/.X*-lock; do disp="${disp#*X}" disp=":${disp%-lock}" # Only add VT-based and xiwi-based chroots here (that excludes Xephyr) if [ "$disp" = ':0' ]; then continue elif DISPLAY="$disp" xprop -root 'XFree86_VT' 2>/dev/null \ | grep -q 'INTEGER'; then displist="$displist $disp" elif DISPLAY="$disp" xprop -root 'CROUTON_XMETHOD' 2>/dev/null \ | grep -q '= "xiwi'; then displist="$displist $disp" xiwiactive='y' fi done # Set to the freon display owner if freon is used freonowner='' if [ ! -f "/sys/class/tty/tty0/active" ]; then if [ -f "$CROUTONLOCKDIR/display" ]; then read -r freonowner < "$CROUTONLOCKDIR/display" fi freonowner="${freonowner:-0}" tty='' else tty="$(cat '/sys/class/tty/tty0/active')" fi # Determine current display if [ "$freonowner" = 0 -o "$tty" = 'tty1' ]; then # In Chromium OS or xiwi chroot curdisp='cros' if [ -n "$xiwiactive" -a -s "$CRIATDISPLAY" ]; then kiwidisp="$(cat "$CRIATDISPLAY")" if [ "${kiwidisp#:[0-9]}" != "$kiwidisp" ]; then curdisp="$kiwidisp" fi fi elif [ -z "$freonowner" ]; then # Poll the displays to figure out which one owns this VT curdisp="$tty" for disp in $displist; do if [ "$disp" = 'cros' ]; then continue fi if DISPLAY="$disp" xprop -root 'XFree86_VT' 2>/dev/null \ | grep -q " ${tty#tty}\$"; then curdisp="$disp" break fi done else # Match the pid to the current freon owner for lockfile in /tmp/.X*-lock; do if grep -q "\\<$freonowner$" "$lockfile"; then curdisp="${lockfile#*X}" curdisp=":${curdisp%%-*}" fi done fi # List the displays if requested if [ "$cmd" = 'l' -o "$cmd" = 'd' ]; then for disp in $displist; do active=' ' if [ "$disp" = "$curdisp" ]; then active='*' if [ "$cmd" = 'd' ]; then echo "$disp" exit 0 fi fi if [ "$cmd" = 'l' ]; then echo -n "$disp$active " getname "$disp" fi done exit 0 fi # Determine the target display if [ -n "${cmd#[pn]}" ]; then if [ "${cmd#:}" != "$cmd" ]; then destdisp="$cmd" else i=0 destdisp='' for disp in $displist; do if [ "$i" -eq "$cmd" ]; then destdisp="$disp" break fi i="$((i+1))" done if [ -z "$destdisp" ]; then error 2 "Display number out of range." fi fi elif [ "$cmd" = 'p' ]; then destdisp="${displist##* }" for disp in $displist; do if [ "$disp" = "$curdisp" ]; then break fi destdisp="$disp" done elif [ "$cmd" = 'n' ]; then destdisp='' for disp in $displist; do if [ -n "$destdisp" ]; then destdisp="$disp" break elif [ "$disp" = "$curdisp" ]; then destdisp="${displist%% *}" fi done if [ -z "$destdisp" ]; then destdisp="${displist%% *}" fi else error 3 "Bad command $cmd." fi # No-op on no-op if [ "$destdisp" = "$curdisp" -a -z "$force" ]; then exit 0 fi # Determine if the target display is on a VT if [ "${destdisp#:}" = "$destdisp" ]; then if [ "$destdisp" != 'cros' ]; then error 3 "Bad destination display $destdisp." fi if [ -z "$freonowner" ]; then export DISPLAY=":0" export XAUTHORITY='/var/host/Xauthority' if [ "$tty" != 'tty1' ]; then sudo -n chvt 1 sleep .1 fi elif [ "${freonowner:-0}" != 0 ]; then kill -USR1 "$freonowner" fi if [ -n "$xiwiactive" ]; then # Release the croutoncycle lock exec 3>&- STATUS="$(echo -n "Xcros" | websocketcommand)" if [ "$STATUS" != 'XOK' ]; then error 1 "${STATUS#?}" fi fi else export DISPLAY="$destdisp" xmethod="$(xprop -root 'CROUTON_XMETHOD' 2>/dev/null \ | sed -n 's/^.*\"\(.*\)\"/\1/p')" if [ "${xmethod%%-*}" = 'xiwi' ]; then if [ -z "$freonowner" -a "$tty" != 'tty1' ]; then sudo -n chvt 1 sleep .1 elif [ "${freonowner:-0}" != 0 ]; then kill -USR1 "$freonowner" fi # Release the croutoncycle lock exec 3>&- STATUS="$(echo -n "X${destdisp} ${xmethod#*-}" | websocketcommand)" if [ "$STATUS" != 'XOK' ]; then error 1 "${STATUS#?}" fi elif [ -z "$freonowner" ]; then dest="$(xprop -root 'XFree86_VT' 2>/dev/null)" dest="${dest##* }" if [ "${dest#[1-9]}" = "$dest" ]; then dest='1' fi # When the destination we are changing to is using fbdev driver in X, we # need first a change to vt 2, else only the session switches and the # display will be stuck on the old vt. sudo -n chvt 2 sudo -n chvt "$dest" else dest="/tmp/.X${destdisp#:}-lock" if [ -f "$dest" ]; then # Trigger the target before releasing the current owner kill -USR1 "$(cat "/tmp/.X${destdisp#:}-lock")" fi if [ "${freonowner:-0}" != 0 ]; then kill -USR1 "$freonowner" fi fi fi if [ -s "$CROUTONLOCKDIR/clip" ]; then kill -USR1 "$(cat "$CROUTONLOCKDIR/clip")" || true fi # Wait a flip and then refresh the display for good measure if [ -n "$DISPLAY" ] && hash xrefresh 2>/dev/null; then sleep .1 xrefresh fi exit 0 ================================================ FILE: chroot-bin/croutonfindnacl ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # croutonfindnacl address signature ["pids"] # # This script is used by croutonfbserver to find the nacl_helper process it is # connected to, and, in particular, the file descriptor corresponding to the shm # memory that the nacl_helper process shares with Chromium. # # - address: NaCl-space address of the shared memory (hexadecimal). We assume # that the NaCl/hardware memory mapping conserves the address, # possibly with a prefix in the MSBs. # - signature: random 8 byte pattern (hexadecimal, machine byte order) that is # written at the beginning of the shared buffer by the NaCl # application. The first 8 bytes of each candidate buffer is read, # guaranteeing that the correct buffer is returned. # - pids: (normally ununsed, defaults to all processes named "nacl_helper") # Space-separated list of PIDs to scan for. # # On success, prints "pid:filename" and exits with code 0. # On error (shm not found, invalid parameters), exits with code >0. set -e VERBOSE= if [ "$#" -lt 2 -o "$#" -gt 3 ]; then echo "Invalid parameters" exit 2 fi ADDRESS="$1" PATTERN="$2" PIDS="${3:-"`pgrep nacl_helper`"}" MATCH="" # Iterate over all NaCl helper processes for pid in $PIDS; do [ -n "$VERBOSE" ] && echo "pid:$pid" 1>&2 # Find candidate mappings file="`awk '$1 ~ /^[0-9a-f]*'"$ADDRESS"'-/ && $2 == "rw-s" \ && $6 ~ /\/shm\/\.(com\.google\.Chrome|org\.chromium\.Chromium)/ \ { print $6 } ' "/proc/$pid/maps"`" [ -n "$VERBOSE" ] && echo "file:$file" 1>&2 if [ -z "$file" ]; then continue fi # Iterate over mappings, and check signature for fd in "/proc/$pid/fd"/*; do link="$(readlink -- "$fd" || true)" link="${link% (deleted)}" if [ "$link" = "$file" ]; then # Check if signature matches pattern="`od -An -t x1 -N8 "$fd" | tr -d ' '`" [ -n "$VERBOSE" ] && echo "FD:$fd ($pattern)" 1>&2 if [ "$pattern" = "$PATTERN" ]; then # Second match? This should never happen if [ -n "$MATCH" ]; then echo -n "-1:ambiguous" exit 1 fi MATCH="$pid:$fd" fi fi done done if [ -n "$MATCH" ]; then echo -n "$MATCH" exit 0 else echo -n "-1:no match" exit 1 fi ================================================ FILE: chroot-bin/croutonnotify ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. USAGE="${0##*/} -j ${0##*/} -t title [-m message] [-d] [-i icon] [-I id] Raises a notification in Chromium OS (requires crouton extension). When -j is specified, reads JSON data from stdin, in the format required by chrome.notifications API, with 2 additional fields, \"crouton_id\" and \"crouton_display\", corresponding respectively to the notification id, and the display to switch to when the notification is clicked (croutoncycle parameter). Otherwise, constructs a \"basic\" notification with the requested fields. Options: -j Read JSON data from stdin (see example below) -t Title to display (chrome.notifications field: \"title\") -m Message to display (chrome.notifications field: \"message\") -d Switch to display in environment variable DISPLAY ($DISPLAY) when the notification is clicked. Default: Do not switch display. -i Small icon to display on the left of the notification (chrome.notifications field: \"iconUrl\") -I ID to pass to chrome.notifications.create: only the last notification with a given ID is shown. Default: Display a new notification. Example JSON data: "'{ "type": "basic", "title": "Primary Title", "message": "Primary message to display", "crouton_display": ":1", "iconUrl": "data:image/png;base64," }' . "$(dirname "$0")/../installer/functions" SWITCHDISPLAY="" MESSAGE="" TITLE="" ID="" ICON="" JSON="" # Process arguments while getopts 'di:I:jm:t:' f; do case "$f" in d) SWITCHDISPLAY="$DISPLAY";; i) ICON="$OPTARG";; I) ID="$OPTARG";; j) JSON="y";; m) MESSAGE="$OPTARG";; t) TITLE="$OPTARG";; \?) error 2 "$USAGE";; esac done # No extra parameters, -j precludes other parameters, and at least title # must be specified (or -j) if [ "$#" != "$((OPTIND-1))" ] || [ "$JSON" = 'y' -a -n "$SWITCHDISPLAY$ICON$ID$MESSAGE$TITLE" ] || [ -z "$JSON$TITLE" ]; then error 2 "$USAGE" fi if [ -n "$ICON" ] && [ ! -r "$ICON" -o ! -f "$ICON" ]; then error 2 "Cannot open $ICON." fi # Escape json string (backslash and double quotes) json_escape() { echo -n "$1" | sed -e 's/\\/\\u005C/g;s/"/\\u0022/g;2~1s/^/\\u000A/;' \ | tr -d '\n' } STATUS="$({ echo -n "N" if [ -z "$JSON" ]; then echo -n '{ "type": "basic", "title": "'"$(json_escape "$TITLE")"'", "message": "'"$(json_escape "$MESSAGE")"'", "crouton_display": "'"$(json_escape "$SWITCHDISPLAY")"'", "crouton_id": "'"$(json_escape "$ID")"'"' if [ -n "$ICON" ]; then ext="${ICON##*.}" if grep -Iq '&2 exit 2 fi exec "$EXEC" "$@";; esac hostdbus='' if hash host-dbus 2>/dev/null; then hostdbus='host-dbus' fi pingpowerd() { $hostdbus dbus-send --system --dest=org.chromium.PowerManager \ --type=method_call /org/chromium/PowerManager \ org.chromium.PowerManager.HandleUserActivity \ int32:0 || true } if [ "$CMD" = 'p' ]; then # Ping pingpowerd exec "${EXEC:-"true"}" "$@" elif [ "$CMD" = 'i' ]; then # Inhibit while pingpowerd; do sleep "$INHIBITSLEEP" done & pid=$! addtrap "kill '$pid' 2>/dev/null" if [ -n "$EXEC" ]; then "$EXEC" "$@" elif [ -n "$2" ]; then shift "$@" else wait $pid fi elif [ -n "$EXEC" ]; then exec "$EXEC" "$@" elif [ "$CMD" = 's' ]; then if hash croutoncycle 2>/dev/null; then croutoncycle cros fi $hostdbus dbus-send --system --dest=org.chromium.PowerManager \ --type=method_call /org/chromium/PowerManager \ org.chromium.PowerManager.RequestSuspend || true elif [ "$CMD" = 'd' ]; then if [ -z "$DISPLAY" ]; then error 1 'Cannot launch daemon: $DISPLAY not specified.' fi # Daemon xdgs='/usr/bin/xdg-screensaver' xi2pid='' # Send pings to powerd at regular intervals, if the user is active (i.e. # there are input events), or if the screensaver is disabled. # For performance reason, we probably do not want to monitor every single # X input events. Therefore, we start a subshell that pings powerd upon # receiving a single event, then quits. # Every $DAEMONSLEEP seconds we check if that subshell if alive, and # restart it if necessary. This means that we miss events during up to # $DAEMONSLEEP seconds, and, if powerd timeout is X seconds, then the # screen may already dim after X-DAEMONSLEEP seconds of inactivity. This is # not noticeable if DAEMONSLEEP is much smaller than X (usually, X=300s). while sleep "$DAEMONSLEEP"; do if [ "`"$xdgs" status 2>/dev/null`" = 'disabled' ]; then # Screensaver disabled: ping pingpowerd elif [ -z "$xi2pid" ] || ! kill -0 "$xi2pid" 2>/dev/null; then # croutonxi2event subshell is not running if [ -n "$xi2pid" ]; then # Fail if return status from process != 0: wait returns the exit # status of the child, and this shell exits on error (-e) wait $xi2pid fi # Wait for input event, then ping immediately ( croutonxi2event -1 >/dev/null 2>&1 && pingpowerd ) & xi2pid=$! fi done fi exit 0 ================================================ FILE: chroot-bin/croutontriggerd ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # Monitors keyboard events for the crouton switch command. . "`dirname "$0"`/../installer/functions" # hexdump output format variables if getconf LONG_BIT | grep -q 32; then HEXDUMP_FMT='2/4 "%u 0 " " " 2/2 "%u " " " 1/4 "%u " "\n"' else HEXDUMP_FMT='4/4 "%u " " " 2/2 "%u " " " 1/4 "%u " "\n"' fi SECONDS_LO='$1' SECONDS_HI='$2' USECONDS_LO='$3' USECONDS_HI='$4' TYPE='$5' KEY='$6' STATE='$7' # constants TYPE_EV_KEY=1 STATE_DOWN=1 STATE_UP=0 KEY_LEFTCTRL=29 KEY_LEFTALT=56 KEY_LEFTSHIFT=42 KEY_RIGHTCTRL=97 KEY_RIGHTALT=100 KEY_RIGHTSHIFT=54 KEY_F1=59 KEY_F2=60 EVENT_DEV_POLL=15 # Only one at a time. xbindkeys lockfile is for legacy compatibility mkdir -m 775 -p "$CROUTONLOCKDIR" exec 3>>"$CROUTONLOCKDIR/xbindkeys" chmod -Rf g+rwX "$CROUTONLOCKDIR" || true chgrp -Rf crouton "$CROUTONLOCKDIR" || true if ! flock -n 3; then echo "Another instance of ${0##*/} running, waiting..." flock 3 fi # Reset event variables to handle strange environments unset `set | grep -o '^event[0-9]*'` 2>/dev/null || true # Poll for new event files and dump the output while :; do # Clean up old hexdumps and start new ones for event in `set | grep -o '^event[0-9]*'` /dev/input/event*; do # Check if the event file is already monitored eval "pid=\"\${${event##*/}:-0}\"" if [ "$pid" != 0 ]; then # Check if it's still running if kill -0 "$pid" 2>/dev/null; then continue fi wait "$pid" || true fi # Clean up old variables if [ "${event#/}" = "$event" ]; then unset "$event" else # Read in the event files and split into input_event fields stdbuf -oL hexdump -e "$HEXDUMP_FMT" "$event" & eval "${event##*/}='$!'" fi done # Avoid picking up the event variable unset event # Kill all event daemons pids="`set | sed -n 's/^event[0-9]*=.\(.*\).$/\1/p' | tr '\n' ' '`" settrap "kill $pids 2>/dev/null;" # Wait for next poll sleep "$EVENT_DEV_POLL" done | unbuffered_awk " function update() { c = lc || rc; s = ls || rs; a = la || ra if (!cmd && c && s && a && p) { cmd = \"p\" } else if (!cmd && c && s && a && n) { cmd = \"n\" } else if (cmd && !c && !s && !a && !p && !n) { system(\"/usr/local/bin/croutoncycle \" cmd) cmd = \"\" } } $TYPE == $TYPE_EV_KEY && $KEY == $KEY_LEFTCTRL { lc = $STATE; update() } $TYPE == $TYPE_EV_KEY && $KEY == $KEY_LEFTSHIFT { ls = $STATE; update() } $TYPE == $TYPE_EV_KEY && $KEY == $KEY_LEFTALT { la = $STATE; update() } $TYPE == $TYPE_EV_KEY && $KEY == $KEY_RIGHTCTRL { rc = $STATE; update() } $TYPE == $TYPE_EV_KEY && $KEY == $KEY_RIGHTSHIFT { rs = $STATE; update() } $TYPE == $TYPE_EV_KEY && $KEY == $KEY_RIGHTALT { ra = $STATE; update() } $TYPE == $TYPE_EV_KEY && $KEY == $KEY_F1 { p = $STATE; update() } $TYPE == $TYPE_EV_KEY && $KEY == $KEY_F2 { n = $STATE; update() } " ================================================ FILE: chroot-bin/croutonurlhandler ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. NEWTABPAGE='chrome://newtab' USAGE="${0##*/} [-n] [URL] Open an URL in Chromium OS (requires crouton extension). If no URL is specified, opens $NEWTABPAGE instead. Switches back to Chromium OS unless -n is specified." . "`dirname "$0"`/../installer/functions" noswitch='' if [ "$1" = '-n' ]; then noswitch='y' shift fi if [ -z "$*" ]; then set -- "$NEWTABPAGE" elif [ "$1" = '-h' -o "$1" = '--help' ]; then error 0 "$USAGE" fi STATUS="`echo -n U"$*" | websocketcommand`" if [ "$STATUS" != 'UOK' ]; then error 1 "${STATUS#?}" fi if [ -z "$noswitch" ]; then croutoncycle cros fi ================================================ FILE: chroot-bin/croutonversion ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. VERSION='unknown' RELEASE='unknown' ARCH='unknown' APPLICATION="${0##*/}" CHANGES='' DOWNLOAD='' UPDATES='' BRANCH="${VERSION##*~}" BRANCH="${BRANCH%%:*}" COMMIT="${VERSION##*:}" CHANGESURL="https://github.com/dnschneid/crouton/compare/$COMMIT..." INSTALLER="crouton-$BRANCH" INSTALLER="${INSTALLER%-master}" CROUTONURL="https://github.com/dnschneid/crouton/raw/releases/$INSTALLER" DEST="$HOME/Downloads/$INSTALLER" USAGE="$APPLICATION [options] Reports the version of crouton installed, checks if updates are available, and/or launches a changelog. If no options are specified, outputs crouton version information to STDOUT. Options: -a Prints the architecture of this chroot to stdout. -c Launches a browser to view the list of changes. If -u is specified, only launches if a newer version exists. -d Downloads the latest version of crouton to the location specified. If -u is specified, only downloads if the version is newer. -f FILE Changes the destination of the downloaded crouton. Default: $DEST -h Prints out usage. -r Prints the release of this chroot to stdout. -u Checks for updates, and prints out the updated version number." # Function to exit with exit code $1, spitting out message $@ to stderr error() { local ecode="$1" shift echo "$*" 1>&2 exit "$ecode" } # Process arguments while getopts 'acdf:ru' f; do case "$f" in a) echo "$ARCH"; exit 0;; c) CHANGES='y';; d) DOWNLOAD='y';; f) DEST="$OPTARG";; r) echo "$RELEASE"; exit 0;; u) UPDATES='y';; \?) error 2 "$USAGE";; esac done # No extra parameters if [ "$#" != "$((OPTIND-1))" ]; then error 2 "$USAGE" fi # Print out version if nothing else specified if [ -z "$CHANGES$DOWNLOAD$UPDATES" ]; then echo "crouton: version $VERSION" echo "release: $RELEASE" echo "architecture: $ARCH" xmethodfile='/etc/crouton/xmethod' if [ -r "$xmethodfile" ]; then echo "xmethod: `cat "$xmethodfile"`" fi targetfile='/etc/crouton/targets' if [ -r "$targetfile" ]; then echo "targets: `sed 's/^,//' "$targetfile"`" fi hostrel='/var/host/lsb-release' if [ -r "$hostrel" ]; then host="`awk -F= '/_RELEASE_DESCRIPTION=/{print $2}' "$hostrel"`" fi echo "host: version ${host:-unknown}" echo "kernel: $(uname -a)" freon="yes" if [ -f /sys/class/tty/tty0/active ]; then freon="no" fi echo "freon: $freon" exit 0 fi # Print out version to stderr for info echo "crouton: version $VERSION" 1>&2 latest='' tmpdir='' if [ -n "$UPDATES$DOWNLOAD" ]; then tmpdir="`mktemp -d --tmpdir=/tmp crouton.XXX`" trap "rm -rf --one-file-system '$tmpdir'" INT HUP 0 echo "Retrieving latest version of $INSTALLER" 1>&2 if ! wget "$CROUTONURL" -O "$tmpdir/$INSTALLER" 2>"$tmpdir/log"; then cat "$tmpdir/log" 1>&2 echo "Failed to retrieve latest version of $INSTALLER" 1>&2 exit 1 fi latest="`awk -F"'" '/^VERSION=/{print $2;exit}' "$tmpdir/$INSTALLER"`" fi # Print out latest version number if requested if [ -n "$UPDATES" ]; then echo "latest: version $latest" elif [ -n "$latest" ]; then echo "latest: version $latest" 1>&2 fi # Save latest version if requested if [ -n "$DOWNLOAD" -a "$VERSION" != "$latest" ]; then echo "Saving latest version to $DEST" 1>&2 mv -f "$tmpdir/$INSTALLER" "$DEST" fi # Launch changelog if requested if [ -n "$CHANGES" -a "$VERSION" != "$latest" ]; then # Check if changelogs are available from this version / to this branch if ! wget "$CHANGESURL$BRANCH" -O/dev/full 2>&1 | grep -q '404 Not Found'; then CHANGESURL="$CHANGESURL$BRANCH" elif ! wget "${CHANGESURL}master" -O/dev/full 2>&1 | grep -q '404 Not Found'; then CHANGESURL="${CHANGESURL}master" else # Fall back on the main commit log CHANGESURL='https://github.com/dnschneid/crouton/commits/' fi # One of these will probably work... for x in exo-open gnome-open kde-open xdg-open \ sensible-browser x-www-browser www-browser; do if hash "$x" 2>/dev/null; then browser="$x" break fi done if [ -z "$browser" ]; then error 2 "No browser found to view $CHANGESURL" fi # Launch the webpage if ! "$browser" "$CHANGESURL"; then error 1 "Failed to launch browser to view $CHANGESURL" fi fi exit 0 ================================================ FILE: chroot-bin/croutonxinitrc-wrapper ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # xinitrc wrapper for crouton: # 1. Runs crouton-specific commands # 2. Runs the provided client (emulating xinit behaviour) # 3. Runs crouton-specific commands before the server is destroyed cmd='' extraargs='' binary='' ret=0 # This part is a translation of what is found in xorg's xinit.c if [ -z "$1" ] || [ "${1#[/.]}" = "$1" ]; then # No client parameter: find .xinitrc if possible, run xterm otherwise required='' if [ -n "$XINITRC" ]; then cmd="$XINITRC" required='y' elif [ -n "$HOME" ]; then cmd="$HOME/.xinitrc" fi if [ ! -e "$cmd" ]; then if [ -n "$required" ]; then echo "Warning, no client init file \"$cmd\"" 1>&2 fi # If no client is given, use default command cmd="xterm" extraargs="-geometry +1+1 -n login" # Make sure xterm is executed directly: let sh resolve the path binary='y' fi else cmd="$1" shift fi # Run crouton-specific commands: # Show chroot specifics for troubleshooting croutonversion 1>&2 if [ -z "$XMETHOD" ]; then if [ -f '/etc/crouton/xmethod' ]; then read -r XMETHOD _ < /etc/crouton/xmethod export XMETHOD else echo 'X11 backend not set.' 1>&2 exit 1 fi fi xmethodtype="${XMETHOD%%-*}" xmethodargs="${XMETHOD#*-}" # Record the name of the chroot in the root window properties if [ -f '/etc/crouton/name' ] && hash xprop 2>/dev/null; then xprop -root -f CROUTON_NAME 8s -set CROUTON_NAME "`cat '/etc/crouton/name'`" fi # Record the crouton XMETHOD in the root window properties xprop -root -f CROUTON_XMETHOD 8s -set CROUTON_XMETHOD "$XMETHOD" # Launch the powerd poker daemon croutonpowerd --daemon & # Launch the clipboard synchronization daemon if hash croutonclip 2>/dev/null; then croutonclip & fi # Launch system-wide trigger daemon croutontriggerd & # Apply the Chromebook keyboard map. Not needed for non-Freon xiwi. if [ "$xmethodtype" != 'xiwi' -o ! -f "/sys/class/tty/tty0/active" ]; then # Apply the Chromebook keyboard map if installed. if [ -f '/usr/share/X11/xkb/compat/chromebook' ]; then setxkbmap -model chromebook fi fi # Input-related stuff is not needed for kiwi if [ "$xmethodtype" != "xiwi" ]; then # Launch X-server-local key binding daemon xbindkeys -fg /etc/crouton/xbindkeysrc.scm # Launch touchegg if it is requested. toucheggconf='/etc/touchegg.conf' if [ -f "$toucheggconf" ]; then mkdir -p "$HOME/.config/touchegg" ln -sf "$toucheggconf" "$HOME/.config/touchegg/" touchegg 2>/dev/null & fi # Configure trackpad settings if needed if synclient >/dev/null 2>&1; then # Elan trackpads usually like these settings if grep -q 'Elan Touchpad' /sys/class/input/event*/device/name; then SYNCLIENT="FingerLow=1 FingerHigh=5 $SYNCLIENT" fi # Other special cases case "`awk -F= '/_RELEASE_BOARD=/{print $2}' '/var/host/lsb-release'`" in butterfly*|eve*|falco*) SYNCLIENT="FingerLow=1 FingerHigh=5 $SYNCLIENT";; parrot*|peppy*|wolf*) SYNCLIENT="FingerLow=5 FingerHigh=10 $SYNCLIENT";; esac if [ -n "$SYNCLIENT" ]; then synclient $SYNCLIENT fi fi fi # Crouton-in-a-tab: Start fbserver and launch display if [ "$xmethodtype" = 'xiwi' ]; then # The extension sends evdev key codes: fix the keyboard mapping rules setxkbmap -rules evdev # Reapply xkb map: This fixes autorepeat mask in "xset q" xkbcomp "$DISPLAY" - | xkbcomp - "$DISPLAY" 2>/dev/null # Set resolution to a default 1024x768, this is important so that the DPI # looks reasonable when the WM/DE start. setres 1024 768 > /dev/null croutonfbserver "$DISPLAY" & try=1 while ! croutoncycle force "$DISPLAY"; do echo "Cannot connect to extension, retrying..." if [ "$try" -ge 10 ]; then echo "\ Unable to start display, make sure the crouton extension is installed and enabled, and up to date. Download from: https://chromewebstore.google.com/detail/crouton-integration/gcpneefbbnfalgjniomfjknbcgkbijom" 1>&2 ret=1 break fi sleep 1 try="$((try+1))" done if [ "$ret" -eq 0 ]; then echo "Connected to extension, launched crouton in a window." 1>&2 fi fi if [ "$xmethodtype" = "xorg" ]; then # Since Chromium 56.0.2923.0, Chromium tries to switch off the display when # switching VT (crbug.com/655770). For some unclear reason, running xrandr # forces the display to be back on, and this is not needed ever again # when switching VTs. # The loop tries to work around a race that is more likely on xenial try=1 while xrandr --auto 2>&1 | grep . 1>&2; do echo "Kicking xrandr again" 1>&2 if [ "$try" -ge 10 ]; then break fi sleep 1 try="$((try+1))" done fi # Only run if no error occured before (e.g. cannot connect to extension) if [ "$ret" -eq 0 ]; then # Shell is the leader of a process group, so signals sent to this process # are propagated to its children. We ignore signals in this process, but the # child handles them and exits. We use a no-op handler, as "" causes the # signal to be ignored in children as well (see NOTES in "man 2 sigaction" # for details). This process then runs exit commands, and terminates. trap "true" HUP INT TERM # Run the client itself if it is executable, otherwise run it in a shell. if [ -n "$binary" -o -x "$cmd" ]; then "$cmd" $extraargs "$@" || ret=$? else /bin/sh "$cmd" $extraargs "$@" || ret=$? fi trap - HUP INT TERM fi # Run crouton-specific commands before the server exits: echo "Running exit commands..." 1>&2 exit "$ret" ================================================ FILE: chroot-bin/gnome-session-wrapper ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # Provides a wrapper around gnome-session to allow gnome-session based # desktop environments to work with crouton nicely USAGE="${0##*/} [session] A wrapper around gnome-session that can be passed to xinit. Provide the session type in the DESKTOP_SESSION variable, or as an optional session argument. Examples: (launch GNOME from crosh with session in DESKTOP_SESSION) DESKTOP_SESSION=gnome xinit /etc/X11/xinit/xinitrc ${0##*/} (launch Unity from an xterm with session as an argument) ${0##*/} ubuntu" export DESKTOP_SESSION="${DESKTOP_SESSION:-"$1"}" export XDG_SESSION_TYPE="${XDG_SESSION_TYPE:-"x11"}" export XDG_SESSION_CLASS="user" SESSION='gnome-session' CINNAMON_SESSION='cinnamon-session' if [ -z "$DESKTOP_SESSION" ]; then echo "$USAGE" 1>&2 exit 2 fi # Cinnamon 2.0 and later uses its own fork of gnome-session if [ "${DESKTOP_SESSION#cinnamon}" != "$DESKTOP_SESSION" ] && \ hash "$CINNAMON_SESSION" 2>/dev/null; then SESSION="$CINNAMON_SESSION" fi SESSION_FILE="/usr/share/$SESSION/sessions/$DESKTOP_SESSION.session" XDG_CURRENT_DESKTOP="$(awk -F "=" '/DesktopName/ {print $2}' "$SESSION_FILE")" if [ -n "$XDG_CURRENT_DESKTOP" ]; then XDG_SESSION_DESKTOP="$XDG_CURRENT_DESKTOP" export XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP fi if [ -z "$DISPLAY" ]; then exec xinit /etc/X11/xinit/xinitrc "$0" else exec "$SESSION" --session="$DESKTOP_SESSION" fi ================================================ FILE: chroot-bin/host-dbus ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Either runs the specified command with the environment set to use the host's # system dbus instance, or prints out the environment changes required. export DBUS_SYSTEM_BUS_ADDRESS='unix:path=/var/host/dbus/system_bus_socket' if [ "$#" = 0 ]; then echo "export DBUS_SYSTEM_BUS_ADDRESS='$DBUS_SYSTEM_BUS_ADDRESS'" else exec "$@" fi ================================================ FILE: chroot-bin/host-wayland ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Either runs the specified command with the environment set to use the host's # wayland server, or prints out the environment changes required. # If wayland-0 socket does not exit, no Chromium OS wayland server exist if [ ! -S "/var/run/chrome/wayland-0" ]; then err="No Chromium OS Wayland server is available." if [ "$#" = 0 ]; then echo "echo '$err' 1>&2" else echo "$err" 1>&2 exit 1 fi else export XDG_RUNTIME_DIR='/var/run/chrome' if [ "$#" = 0 ]; then echo "export XDG_RUNTIME_DIR='$XDG_RUNTIME_DIR'" else exec "$@" fi fi ================================================ FILE: chroot-bin/setres ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Changes the resolution of the current display. # If XMETHOD is xiwi, tries to create a new exact resolution, and change mode # to that. If that fails (e.g. non-patched xorg-dummy), take the closest, # smaller, available resolution in xrandr, and if no smaller resolution is # available, pick the closest one. # If XMETHOD is anything else, set a resolution from cvt output. # In all cases, outputs the applied resolution. set -e if [ "$#" -lt 2 -o "$#" -gt 4 ]; then echo "USAGE: ${0##*/} x y [r [output]]" 1>&2 exit 2 fi x="$1" y="$2" r="${3:-60}" o="${4}" if [ -z "$o" ]; then o="`xrandr -q | awk 'x{print $1;exit}/^Screen 0/{x=1}'`" fi xmethod="`xprop -root 'CROUTON_XMETHOD' | sed -n 's/^.*\"\(.*\)\"/\1/p'`" if [ "${xmethod%%-*}" != "xiwi" ]; then cvt "$x" "$y" "$r" | { read -r _ read -r _ mode data mode="${mode#\"}" mode="${mode%\"}" xrandr --newmode "$mode" $data 2>/dev/null || true xrandr --addmode "$o" "$mode" xrandr --output "$o" --mode "$mode" echo "$mode" } exit 0 fi # Replace mode $2 in output $1, with new data $3..$# # Deletes the mode if $3 is not provided replacemode() { local o="$1" local mode="$2" shift 2 xrandr --delmode "$o" "$mode" 2>/dev/null || true xrandr --rmmode "$mode" 2>/dev/null || true if [ "$#" -gt 0 ]; then xrandr --newmode "$mode" "$@" xrandr --addmode "$o" "$mode" fi } # Try to change to arbitrary resolution mhz="$((r*x*y/1000000))" name="kiwi_${x}x${y}_${r}" # Try to switch mode, if it already exists. if xrandr --output "$o" --mode "$name" 2>/dev/null; then echo "${x}x${y}_${r}" exit 0 fi # Add the new mode xrandr --newmode "$name" $mhz $x $x $x $x $y $y $y $y xrandr --addmode "$o" "$name" # The next line fails on non-patched xorg-dummy if xrandr --output "$o" --mode "$name"; then # Success: remove old modes others="`xrandr | sed -n 's/^.*\(kiwi[0-9x_]*\)[^*]*$/\1/p'`" for othername in $others; do xrandr --delmode "$o" "$othername" 2>/dev/null || true xrandr --rmmode "$othername" 2>/dev/null || true done echo "${x}x${y}_${r}" exit 0 else # Delete the new mode xrandr --delmode "$o" "$name" 2>/dev/null || true xrandr --rmmode "$name" 2>/dev/null || true fi # Probably xorg-dummy got overwritten. Recommend an update. echo "Failed to set custom resolution. Update your chroot and try again." 1>&2 exit 1 ================================================ FILE: chroot-bin/startgnome ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Launches GNOME; automatically falls back to gnome-panel exec crouton-noroot gnome-session-wrapper gnome ================================================ FILE: chroot-bin/startunity ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Launches Unity; will fall back to Unity-2D on supported releases # Ensure global app menus work. export UBUNTU_MENUPROXY=1 export GTK_MODULES="unity-gtk-module" exec crouton-noroot gnome-session-wrapper ubuntu ================================================ FILE: chroot-bin/volume ================================================ #!/bin/sh # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # This script uses amixer to present an interface similar to the brightness # script for crouton. set -e set -u DEFAULT_VOLUME_DELTA=5 APPLICATION="${0##*/}" USAGE="$APPLICATION [set] [0-100] $APPLICATION up|down [0-100] $APPLICATION mute [set|unset|toggle] $APPLICATION get [all|volume|mute] This script changes the volume of the current output device, as would changing it with the shortcut keys or with the GUI in Chrome OS. Shortcut invocations: $APPLICATION up increases by $DEFAULT_VOLUME_DELTA $APPLICATION down decreases by $DEFAULT_VOLUME_DELTA $APPLICATION mute sets muted $APPLICATION 0-100 sets volume to given value $APPLICATION get shortcut for get all " # The control which controls the current output. ALSA_CONTROL="Master" # Tiny sugar coating around amixer for common parameters. _amixer() { amixer -Dcras "$@" } amixer_get_value() { # $1 is the control name to get the value for _amixer cget "name=$1" | sed -n \ -e "/[[:space:]]\+:/s/.*=//p" # ^ ^ ^ Print the line. # ^ ^ Replace everything before the equal sign. # ^ Search for the line with spaces and a colon (the value). } get_volume() { amixer_get_value "$ALSA_CONTROL Playback Volume" } set_volume() { local volume="$1" _amixer -q sset $ALSA_CONTROL "$volume" } get_is_muted() { # Get the alsa state of the control. local state="$(amixer_get_value "$ALSA_CONTROL Playback Switch")" # Muted is off (control is off) if [ "$state" = "on" ]; then # Is not muted, so is_muted is false return 1 else # Is muted, so is_muted is true return 0 fi } get_is_muted_as_text() { if get_is_muted; then echo "yes" else echo "no" fi } toggle_mute() { _amixer -q sset $ALSA_CONTROL toggle } mute() { _amixer -q sset $ALSA_CONTROL mute } unmute() { _amixer -q sset $ALSA_CONTROL unmute } relative_volume() { local delta="$1" # Check that this is integer-ish enough. if ! [ "$delta" -eq "$delta" ] 2>/dev/null; then error_help "Error: $APPLICATION needs a number [0-100]" exit 1 fi if get_is_muted; then if [ "$delta" -lt 0 ]; then set_volume 0 fi unmute else # Is the volume going down or up? if [ "$delta" -lt 0 ]; then # Going down, we strip the leading minus sign, # And suffix the minus sign. set_volume "${delta#*-}-" else # Only suffix the plus sign. set_volume "${delta}+" fi fi } print_help() { echo "$USAGE" } error_help() { # Prints an error message and the usage to stderr [ $# -gt 0 ] && echo "$@" 1>&2 print_help 1>&2 } # Do a sanity check with amixer. # An out of date crouton chroot might exhibit problems like: # amixer: Control cras element read error: Input/output error # We cannot do it in _amixer since it's executed in a subshell and its value is # used as a substitution. if ! _amixer > /dev/null 2>&1; then echo "Failed to communicate with the audio server. Please update your chroot." 1>&2 exit 2 fi if [ $# -lt 1 ]; then error_help "Error: $APPLICATION needs at least a command." exit 1 fi cmd="$1" shift case "$cmd" in h*|-h*|--help) print_help ;; up) relative_volume "${1:-$DEFAULT_VOLUME_DELTA}" ;; down) relative_volume "-${1:-$DEFAULT_VOLUME_DELTA}" ;; mute) action="${1-set}" case "$action" in toggle) toggle_mute ;; set) mute ;; unset) unmute ;; *) error_help "Invalid action: $action for mute." exit 1 ;; esac ;; set|[0-9]|[0-9][0-9]|100) amount="$cmd" # First, check that we received a value to set the volume to. if [ $# -eq 0 ] && [ "$cmd" = "set" ]; then error_help "Error: $APPLICATION set needs an amount to set." exit 1 fi # The value /could/ have been a parameter or the command itself. if [ $# -gt 0 ]; then amount="$1" shift fi # Check that this is integer-ish enough. if ! [ "$amount" -eq "$amount" ] 2>/dev/null; then error_help "Error: $APPLICATION set needs a number [0-100]" exit 1 fi set_volume "$amount" ;; get) action="${1-all}" case "$action" in mute) get_is_muted_as_text ;; volume) get_volume ;; all) echo "Volume: $(get_volume)" echo "Muted: $(get_is_muted_as_text)" ;; *) error_help "Invalid action: $action for get." exit 1 ;; esac ;; *) error_help "Error: Unkown command $cmd" exit 1 ;; esac ================================================ FILE: chroot-bin/xinit ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Adds a :# to the xinit command line, where # is the first available display # number. Also adds the -- to the command line and references the global # xserverrc if it isn't already there. By putting this in /usr/local/bin, PATH # will prefer it and scripts that call xinit will automagically work. xserverrc='/etc/X11/xinit/xserverrc' dash='--' for arg in "$@"; do if [ -z "$dash" ]; then # Check if there's a xserverrc specified. if [ "${arg#/}" != "$arg" ]; then xserverrc='' fi break elif [ "$arg" = '--' ]; then dash= fi done # Never use display :0 (confusing if aura does not use X11) disp=1 while [ -f "/tmp/.X$disp-lock" ]; do disp=$((disp+1)) done # If possible, switch to VT1 to avoid strangeness when launching from VT2 chvt 1 2>/dev/null || true exec /usr/bin/xinit /usr/local/bin/croutonxinitrc-wrapper "$@" $dash $xserverrc ":$disp" ================================================ FILE: chroot-bin/xiwi ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Runs the specified X11 application in its own X server in Chromium OS. USAGE="Usage: ${0##*/} [-f] [-F|-T] APPLICATION [PARAMETERS ...] Launches a windowed session in Chromium OS for any graphical application. Applications launched in this way show in independent windows or tabs. All parameters are passed to the specified application. By default, the app is launched in a window. Options: -F Launch the APPLICATION full-screen. -T Launch the APPLICATION in a tab. -f Prevent ${0##*/} from quitting automatically. (see NOTE below) NOTE: ${0##*/} will normally close when the application returns. Some gui applications fork before or during normal operation, which can confuse ${0##*/} and cause it to quit prematurely. If your application does not have a parameter that prevents it from forking, and crouton is unable to automatically detect the fork, you can use -f to prevent ${0##*/} from quitting automatically. ${0##*/} will quit if you close the Chromium OS window when nothing is displayed. A default window manager will full-screen all windows, unless APPLICATION begins with 'start' or is 'xinit'. You can cycle through multiple windows inside the application via Ctrl-Alt-Tab/Ctrl-Alt-Shift-Tab, or close them via Ctrl-Alt-Shift-Escape. If APPLICATION begins with 'start' but you still want to use the default window manager, specify the full path of the application." . "`dirname "$0"`/../installer/functions" xiwicmd="`readlink -f -- "$0"`" OPTSTRING='FfTt' if [ "$#" = 0 ]; then error 2 "$USAGE" elif [ "$1" = '/' ]; then shift 1 foreground='' while getopts "$OPTSTRING" f; do case "$f" in f) foreground='y';; t|T|F) :;; \?) error 2 "$USAGE";; esac done shift "$((OPTIND-1))" xsetroot -cursor_name left_ptr if [ "$1" != 'xinit' -a "${1#start}" = "$1" ]; then i3 -c "/etc/crouton/xiwi.conf" & # Wait for i3 to launch xprop -spy -root | grep -q _NET_ACTIVE_WINDOW # Launch the window title monitoring daemon # _NET_ACTIVE_WINDOW is more reliable than _NET_CLIENT_LIST_STACKING for # keeping track of the topmost window. xprop -spy -notype -root 0i ' $0\n' '_NET_ACTIVE_WINDOW' 2>/dev/null | { name="`cat /etc/crouton/name`" monpid='' monwid='' while read _ wid; do if [ "$wid" = "$monwid" ]; then continue fi if [ -n "$monpid" ]; then kill "$monpid" 2>/dev/null fi monwid="$wid" (xprop -spy -notype -id "$wid" 'WM_NAME' 2>/dev/null || echo) \ | while read _ title; do title="${title%\"}" xprop -root -f CROUTON_NAME 8s -set CROUTON_NAME \ "$name/$1${title:+": "}${title#*\"}" { echo -n 'C' croutoncycle l } | websocketcommand >/dev/null done & monpid="$!" done if [ -n "$monpid" ]; then kill "$monpid" 2>/dev/null fi } & # Launch user init scripts if [ -f "$HOME/.xiwirc" ]; then /bin/sh "$HOME/.xiwirc" || true fi fi starttime="$(date +%s)" "$@" endtime="$(date +%s)" if [ -n "$foreground" -o "$(($endtime-$starttime))" -le 2 ]; then xprop -spy -notype -root 0i ' $0\n' 'CROUTON_CONNECTED' \ | while read _ connected; do if [ "$connected" != 0 ]; then continue fi # _NET_CLIENT_LIST_STACKING is more reliable than # _NET_ACTIVE_WINDOW for detecting when no windows exist if ! xprop -notype -root '_NET_CLIENT_LIST_STACKING' \ | grep -q '0x'; then kill "$$" break fi done fi else export XMETHOD='xiwi-window' while getopts "$OPTSTRING" f; do case "$f" in f) :;; F) export XMETHOD='xiwi-fullscreen';; t|T) export XMETHOD='xiwi-tab';; \?) error 2 "$USAGE";; esac done eval "exe=\"\$$OPTIND\"" if ! hash "$exe" 2>/dev/null; then error 2 "${0##*/}: $exe: not found" fi exec /usr/local/bin/xinit "$xiwicmd" / "$@" fi ================================================ FILE: chroot-etc/kodi-cycle.py ================================================ #!/usr/bin/env python # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # Python script to call croutoncycle. This is needed to let the # hotkeys ctr-shift-alt F1/F2 work when kodi is in fullscreen. import subprocess import sys if len(sys.argv) == 2 and sys.argv[1] in ("prev", "next"): exitcode = subprocess.call(["/usr/local/bin/croutoncycle", sys.argv[1]]) else: sys.stderr.write("Usage: %s prev|next\n" % str(sys.argv[0])) exitcode = 2 sys.exit(exitcode) ================================================ FILE: chroot-etc/kodi-keyboard.xml ================================================ RunScript(/etc/crouton/kodi-cycle.py,prev) RunScript(/etc/crouton/kodi-cycle.py,next) ================================================ FILE: chroot-etc/pulseaudio-default.pa ================================================ #!/usr/bin/pulseaudio -nF # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Include default configuration first .include /etc/pulse/default.pa # Forward audio to Chromium OS audio server load-module module-alsa-sink device=cras sink_name=cras-sink load-module module-alsa-source device=cras source_name=cras-source set-default-sink cras-sink set-default-source cras-source ================================================ FILE: chroot-etc/unity-autostart.desktop ================================================ # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. [Desktop Entry] Type=Application Name=crouton autostart script for Unity Exec=/usr/local/bin/crouton-unity-autostart OnlyShowIn=Unity; NoDisplay=true X-GNOME-Autostart-Phase=Initialization X-GNOME-Autostart-Notify=true X-GNOME-AutoRestart=true ================================================ FILE: chroot-etc/unity-profiled ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # Helper script for Unity called at user login. # Right now this just sets up .desktop files in the user's autostart # directory to override global autostart. RELEASE="`/usr/local/bin/croutonversion -r`" # unity-settings-daemon should run instead of gnome-settings-daemon in trusty if [ "$RELEASE" = 'trusty' -o "$RELEASE" = 'xenial' ]; then autostartdir="$HOME/.config/autostart" mkdir -p "$autostartdir" cat > "$autostartdir"/gnome-settings-daemon.desktop <&2 exit 1 fi fi xserverrc="/etc/crouton/xserverrc-${XMETHOD%%-*}" if [ "${XMETHOD##*/}" != "$XMETHOD" -o ! -f "$xserverrc" ]; then echo "Invalid X11 backend '$XMETHOD'" 1>&2 exit 2 fi . "$xserverrc" ================================================ FILE: chroot-etc/xserverrc-local.example ================================================ #!/bin/sh # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Sample script to customize X server invocation. To activate copy it to # /etc/crouton/xserverrc-local # # The file is sourced before invoking the X server with the variable # XMETHOD set to xiwi, or xorg and the variable XARGS containing the # command line arguments that will be passed to the server. # # Uncoment if fonts look too big on machines with 1366x768 11.6" screen #XARGS="$XARGS -dpi 135x135" ================================================ FILE: chroot-etc/xserverrc-xiwi ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. logfile="/tmp/Xorg.crouton.$$.log" for arg in "$@"; do disp="`echo "$arg" | sed -n 's/^\:\([0-9]*\)$/\1/p'`" if [ -n "$disp" ]; then logfile="/tmp/Xorg.crouton.$disp.log" fi done if [ "${XMETHOD%%-*}" != 'xiwi' ]; then export XMETHOD='xiwi' fi XARGS="-nolisten tcp -config xorg-dummy.conf -logfile $logfile" if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local fi exec /usr/bin/Xorg $XARGS "$@" ================================================ FILE: chroot-etc/xserverrc-xorg ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. if [ "${XMETHOD%%-*}" != 'xorg' ]; then export XMETHOD='xorg' fi XARGS='-nolisten tcp' if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local fi X=/usr/bin/X # Handle Freon systems if [ ! -f "/sys/class/tty/tty0/active" ]; then # We won't be able to launch properly if running from frecon ppid="$$" while [ -n "$ppid" -a "$ppid" -ne 1 ]; do ppid="`ps -p "$ppid" -o 'ppid=' 2>/dev/null | sed 's/ //g'`" if ps -p "$ppid" -o 'comm=' | grep -q '^frecon$'; then echo 'Xorg X11 servers cannot be launched from Frecon.' 1>&2 echo 'Return to Chromium OS and use crosh to launch X.' 1>&2 exit 2 fi done # Prepare lock file mkdir -p '/tmp/crouton-lock' touch '/tmp/crouton-lock/display' chmod -Rf g+rwX '/tmp/crouton-lock' chgrp -Rf crouton '/tmp/crouton-lock' # Freon necessitates the preload hack for X to coexist X=/usr/bin/Xorg logfile="/tmp/Xorg.crouton.$$.log" for arg in "$@"; do disp="`echo "$arg" | sed -n 's/^\:\([0-9]*\)$/\1/p'`" if [ -n "$disp" ]; then logfile="/tmp/Xorg.crouton.$disp.log" fi done XARGS="$XARGS -logfile $logfile" export LD_PRELOAD="/usr/local/lib/croutonfreon.so:$LD_PRELOAD" fi exec "$X" $XARGS "$@" ================================================ FILE: host-bin/crash_reporter_wrapper ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # When launched from the kernel, PATH is not set and stderr is closed if [ ! -t 0 ]; then exec 2>/var/log/crash_reporter_wrapper.log export PATH='/bin:/sbin:/usr/bin:/usr/sbin' fi APPLICATION="${0##*/}" CORE_PATTERN_PARAMS='%p %s %u %g %e %h %t %E' CORE_PATTERN_PARAMS_COUNT=8 CORE_PATTERN="|`readlink -f -- "$0"` $CORE_PATTERN_PARAMS" CORE_PATTERN_FILE='/var/run/crw/core_pattern' CORE_PATTERN_PROC='/proc/sys/kernel/core_pattern' DEFAULT_CHROOT_PATTERN='' IDEAL_LOCATION='/var/run/crw/crw' USAGE="$APPLICATION register $APPLICATION $CORE_PATTERN_PARAMS Handles system core dumps, passing the dump through to Chromium OS's crash_reporter or processing it as the chroot requests, depending on what namespace the process belongs to. The first form should be run as root, and sets $CORE_PATTERN_PROC to use this script as the core dump pipe program. The second form is expected to be called by the kernel upon program crash, via the core_pattern set with the first form. The script checks if the crashing PID's root has an /etc/crouton/core_pattern and uses that to emulate the kernel's handling of the core_pattern proc entry." # Outputs a parameter by core_pattern name (e.g. %p prints out the expanded $1) # If the parameter doesn't exist, outputs an error to stderr and returns $1 # $1: the parameter name, either in the form of "%p" or just "p" # $2+: must be "$@" get_parameter() { local param="%${1#%}" param_names="$CORE_PATTERN_PARAMS " if [ "$param" = '%%' ]; then echo -n '%' return elif [ "$param" = '%c' ]; then echo -n "$core_limit" return fi shift while [ "$param" != "${param_names%% *}" ]; do param_names="${param_names#* }" if [ -z "$param_names" ]; then echo "$param not provided for core_pattern" 1>&2 echo -n "$param" return fi shift done echo -n "$1" } # Takes the specified string and expands included parameters # Outputs result to stdout # $1: the string to expand and escape # $2+: must be "$@" expand_parameters() { local unexpanded="$1%" remain param shift while [ -n "$unexpanded" ]; do param="${unexpanded%%%*}" echo -n "$param" remain="${unexpanded#*%?}" if [ "$remain" = "$unexpanded" ]; then break fi param="${unexpanded#"$param"}" param="${param%"$remain"}" unexpanded="$remain" get_parameter "$param" "$@" done } # Takes stdin and escapes it to be safe within single-quotes # Outputs result to stdout # Converts ' to '\'' escape() { sed "s/'/'\\\\\\''/g" } if [ "$#" != 1 -a "$#" != "$CORE_PATTERN_PARAMS_COUNT" ]; then echo "$USAGE" 1>&2 exit 2 fi if [ "$#" = 1 -a "$1" != 'register' ]; then echo "$USAGE" 1>&2 exit 2 fi if [ "${UID:-0}" != 0 -o "${USER:-root}" != 'root' ]; then echo "$APPLICATION must be run as root." 1>&2 exit 2 fi if [ "$#" = 1 ]; then # Always register the script from /var/run for safety if [ "$0" != "$IDEAL_LOCATION" ]; then dir="${IDEAL_LOCATION%/*}" if ! mountpoint "$dir" >/dev/null 2>/dev/null; then mkdir -p "$dir" mount -t tmpfs \ -o 'rw,nosuid,nodev,exec,noatime,mode=700,size=128K' \ tmpfs "$dir" fi cp -fT "$0" "$IDEAL_LOCATION" chmod 500 "$IDEAL_LOCATION" exec "$IDEAL_LOCATION" "$@" fi # Store Chromium OS's core pattern for passthrough usage if [ ! -f "$CORE_PATTERN_FILE" ]; then cat "$CORE_PATTERN_PROC" > "$CORE_PATTERN_FILE" fi # Register ourselves as the coredump handler echo "$CORE_PATTERN" > "$CORE_PATTERN_PROC" exit 0 fi # It's a core dump! See CORE(5) for details in emulating core_pattern. pattern='' # $1 is the pid of the process. Check the process's root for etc/crouton pid="$1" root="/proc/$pid/root" cwd="/proc/$pid/cwd" croutondir="$root/etc/crouton" if [ -d "$croutondir" ]; then # Looks like a chroot (or a very weird rootfs, but that's unlikely) # Grab the chroot's pattern or use the default if the file doesn't exist if [ -f "$croutondir/core_pattern" ]; then # File exists; first non-empty, non-comment line is the core_pattern pattern="`awk '/^[^#]/ { print $0; exit }' "$croutondir/core_pattern"`" else pattern="$DEFAULT_CHROOT_PATTERN" fi else # Probably a Chromium process. Grab the backed-up core pattern. if [ -f "$CORE_PATTERN_FILE" ]; then pattern="`cat "$CORE_PATTERN_FILE"`" else echo "$APPLICATION was not properly registered as the core_pattern." 1>&2 echo "You must register using '$0 register'" 1>&2 exit 2 fi fi # If the pattern is empty, just exit. if [ -z "$pattern" ]; then exit 0 fi # Remove the pipe character so we can play with the path generically ispipe='' if [ "${pattern#|}" != "$pattern" ]; then ispipe='y' pattern="${pattern#|}" # Pipe paths must be absolute if [ "${pattern#/}" = "$pattern" ]; then echo "core_pattern '|$pattern' is not absolute" 1>&2 exit 2 fi fi # Get the core size limit for the process core_limit="`awk '/^Max core file size/ { printf ($5=="unlimited") ? -1 : $5; exit }' "/proc/$pid/limits"`" # Prepare the file to operate on if [ -z "$ispipe" ]; then # Don't bother if core file size limit is 0 if [ "$core_limit" = 0 ]; then exit 0 fi # If this is not a pipe, we need to expand the %'s here. file="`expand_parameters "$pattern" "$@"`" else # For pipes, path is anything up to a space file="${pattern%% *}" pattern="${pattern#"$file"}" fi # Fix up the path to be working directory-relative if [ "${file#/}" = "$file" ]; then file="$cwd/$file" fi # Canonicalize within the chroot and escape the filename to work within quotes file="`chroot "$root" readlink -m -- "/${file%/*}"`/${file##*/}" file="`echo -n "$file" | escape`" # Prepare the command to run inside the chroot as the appropriate user if [ -z "$ispipe" ]; then cmd=" if [ -e '$file' -a ! -f '$file' ] || [ -h '$file' ]; then echo \"Not overwriting '$file'; it is not a regular file\" 1>&2 elif [ -f '$file' ] && [ \"\`stat -c '%h' '$file'\`\" != 1 ]; then echo \"Not overwriting '$file'; it is multiply linked\" 1>&2 elif [ '$core_limit' = -1 ] && cat > '$file'; then exit 0 elif [ '$core_limit' != -1 ] && head -c '$core_limit' > '$file'; then exit 0 fi exit 1 " username="`chroot "$root" ps -ouser= -p "$pid"`" else # Finally need to expand the parameters here cmd="exec '$file'" params="$pattern " while [ -n "$params" ]; do param="${params%% *}" params="${params#* }" if [ -n "$param" ]; then cmd="$cmd '`expand_parameters "$param" "$@" | escape`'" fi done username='root' fi # Run the generated command within the appropriate chroot exec chroot "$root" su -s '/bin/sh' -c "$cmd" - "$username" ================================================ FILE: host-bin/edit-chroot ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" ALLCHROOTS='' BACKUP='' BINDIR="`dirname "\`readlink -f -- "$0"\`"`" CHROOTS="`readlink -m -- "$BINDIR/../chroots"`" DELETE='' ENCRYPT='' KEYFILE='' LISTDETAILS='' MOVE='' COPY='' NEEDS_UNMOUNT='' RESTORE='' SPLIT='' TARBALL='' YES='' YESPARAM='' USAGE="$APPLICATION [options] name [...] Edits a chroot. Options: -a Operates on all chroots in $CHROOTS. If no other operation is specified, prints out the names of the chroots. -c CHROOTS Directory the chroots are in. Default: $CHROOTS -b Backs up the chroot to a tarball. Compression format is chosen based on the tarball extension. Backups always take place before other actions on a given chroot. -d Deletes the chroot. Assumed if run as delete-chroot. -e If the chroot is not encrypted, encrypt it. If it is encrypted, change the encryption passphrase. -f TARBALL When used with -b, overrides the default tarball to back up to. If unspecified, assumes NAME-yyyymmdd-hhmm.tar[.gz], where .gz is included for unencrypted chroots, and not for encrypted ones. When used with -r, specifies the tarball to restore from. If TARBALL is a directory, automatic naming is still used. If multiple chroots are specified, TARBALL must be a directory. -k KEYFILE File or directory to store the (encrypted) encryption keys in. If unspecified, the keys will be stored in the chroot if doing a first encryption, or left in place on existing chroots. If specified, keyfile will be moved. Specify a dash - as the KEYFILE to move the key back into the chroot. If multiple chroots are specified, KEYFILE must either be - or a directory. -l Prints out croutonversion details on the chroot, if available. Specify twice to prompt and unlock encrypted chroots as necessary. -m DEST Moves a chroot. Specify a new name to keep it in the same directory, or an absolute path to move it entirely. DEST can be a directory, in which case it must end in a slash. If multiple chroots are specified, DEST must be a directory. If you are moving a chroot to a SD card/USB drive, make sure the storage is formatted to ext2/3/4. -C DEST Like -m, but copies instead of moves. -r Restores a chroot from a tarball. The tarball path can be specified with -f or detected from name. If both are specified, restores to that name instead of the one in the tarball. Will not overwrite a chroot when restoring unless -r is specified twice. -s SPLIT Force a backup archive to be split into SPLIT-sized chunks. SPLIT is specified in megabytes (1048576 bytes), and cannot be smaller than 10. FAT32 filesystems are split by default to fit within 4GB. -y Do all actions without confirmation." # Common functions . "$BINDIR/../installer/functions" # Process arguments getopts_string='abc:C:def:k:lm:rs:y' while getopts_nextarg; do case "$getopts_var" in a) ALLCHROOTS='y';; b) BACKUP='y'; NEEDS_UNMOUNT='y';; c) CHROOTS="`readlink -m -- "$getopts_arg"`";; C) COPY="$getopts_arg"; NEEDS_UNMOUNT='y';; d) DELETE='y'; NEEDS_UNMOUNT='y';; e) ENCRYPT='y'; NEEDS_UNMOUNT='y';; f) TARBALL="$getopts_arg";; k) KEYFILE="$getopts_arg";; l) LISTDETAILS=$(($LISTDETAILS+1));; m) MOVE="$getopts_arg"; NEEDS_UNMOUNT='y';; r) RESTORE=$(($RESTORE+1)); NEEDS_UNMOUNT='y';; s) SPLIT="$getopts_arg";; y) YES='a'; YESPARAM='-y';; \?) error 2 "$USAGE";; esac done # If the executable name is delete*, assume DELETE. if [ "${APPLICATION#delete}" != "$APPLICATION" ]; then DELETE='y' NEEDS_UNMOUNT='y' fi # At least one command must be specified if [ -z "$ALLCHROOTS$BACKUP$DELETE$ENCRYPT$KEYFILE$LISTDETAILS$MOVE$COPY$RESTORE" ]; then error 2 "$USAGE" fi # Need at least one chroot listed if not using -r with -f or -a. if [ $# = 0 ] && ! [ -n "$RESTORE" -a -n "$TARBALL" -o -n "$ALLCHROOTS" ]; then error 2 "$USAGE" fi # Cannot specify a chroot and -a. if [ $# -gt 0 -a -n "$ALLCHROOTS" ]; then error 2 "$USAGE" fi # -f without -r or -b doesn't make sense if [ -n "$TARBALL" -a -z "$BACKUP$RESTORE" ]; then error 2 "$USAGE" fi # Cannot specify both backup and restore. if [ -n "$BACKUP" -a -n "$RESTORE" ]; then error 2 "$USAGE" fi # Cannot specify both copy and move. if [ -n "$COPY" -a -n "$MOVE" ]; then error 2 "$USAGE" fi # Cannot specify delete with anything else. if [ -n "$DELETE" -a -n "$BACKUP$ENCRYPT$KEYFILE$MOVE$COPY$RESTORE" ]; then error 2 "$USAGE" fi # Make sure SPLIT is reasonable if [ -n "$SPLIT" ] && [ "$SPLIT" -lt 10 -o -z "$BACKUP" ]; then error 2 "$USAGE" fi # If we specified -a option, bring in all chroots. if [ -n "$ALLCHROOTS" ]; then if [ ! -d "$CHROOTS" ]; then error 1 "$CHROOTS not found." fi for crname in "$CHROOTS"/*; do if [ -d "$crname" ]; then set -- "$@" "${crname##*/}" fi done if [ $# = 0 ]; then error 1 "No chroots found in $CHROOTS" fi fi # If TARBALL is unspecified and we're in /, put the tarball in ~/Downloads if [ -n "$BACKUP$RESTORE" -a -z "$TARBALL" -a "$PWD" = '/' \ -a -d '/home/chronos/user/Downloads' ]; then TARBALL="/home/chronos/user/Downloads/" fi # If multiple chroots are listed, KEYFILE and MOVE and COPY must be empty or directories. if [ $# -gt 1 -a -f "$KEYFILE" -a "$KEYFILE" != '-' ]; then error 2 "Multiple chroots specified, but $KEYFILE is not a directory." elif [ $# -gt 1 -a -n "$MOVE" -a "${MOVE%/}" = "$MOVE" ]; then error 2 "Multiple chroots specified, but $MOVE is not a directory." elif [ $# -gt 1 -a -n "$COPY" -a "${COPY%/}" = "$COPY" ]; then error 2 "Multiple chroots specified, but $COPY is not a directory." elif [ $# -gt 1 -a -f "$TARBALL" ]; then error 2 "Multiple chroots specified, but $TARBALL is not a directory." fi # Don't allow moving to non-ext filesystems (but don't check if just renaming) if [ -n "$MOVE" -a "${MOVE#*/}" != "$MOVE" ] && \ df -T "`getmountpoint "$MOVE"`" | awk '$2~"^ext"{exit 1}'; then error 2 "Chroots can only be moved to ext filesystems." fi # Don't allow copying to non-ext filesystems (but don't check if in same directory) if [ -n "$COPY" -a "${COPY#*/}" != "$COPY" ] && \ df -T "`getmountpoint "$COPY"`" | awk '$2~"^ext"{exit 1}'; then error 2 "Chroots can only be copied to ext filesystems." fi # Don't allow restoring to non-ext filesystems if [ -n "$RESTORE" ] && \ df -T "`getmountpoint "$CHROOTS"`" | awk '$2~"^ext"{exit 1}'; then error 2 "Chroots can only be restored to ext filesystems." fi # Don't allow backing up to tmpfs filesystems if [ -n "$BACKUP" ] && ! df -T "`getmountpoint "${TARBALL:-.}"`" \ | awk '$2=="tmpfs"{exit 1}'; then error 2 "Chroots cannot be backed up to temporary filesystems." fi # We need to run as root if [ "$USER" != root -a "$UID" != 0 ]; then error 2 "$APPLICATION must be run as root." fi # Strongly advise against moving a keyfile to a tmpfs path if [ "${KEYFILE:--}" != '-' ] && \ ! df -T "`getmountpoint "$KEYFILE"`" | awk '$2=="tmpfs"{exit 1}'; then echo -n '\ Moving a keyfile to a temporary filesystem is a really good way to permanently lose access to your chroot. If you still want to do this, wait for 15 seconds. Otherwise: HIT CTRL-C RIGHT NOW > ' 1>&2 sleep 15 echo \ '...okay. Be sure to put the keyfile somewhere safe before you reboot.' 1>&2 fi # If we're restoring and specified a tarball and no name, detect the name. if [ -n "$RESTORE" -a -n "$TARBALL" -a $# = 0 ]; then echo 'Detecting chroot name...' 1>&2 label="`tar --test-label -f "$TARBALL" 2>/dev/null`" if [ -n "$label" ]; then if [ "${label#crouton:backup}" = "$label" ]; then error 2 "$TARBALL doesn't appear to be a valid crouton backup." fi NAME="${label#*-}" else # Old backups just use the first folder name NAME="`tar -tf "$TARBALL" 2>/dev/null | head -n 1`" NAME="${NAME%%/*}" fi if [ -z "$NAME" ]; then error 2 "$TARBALL doesn't appear to be a valid tarball." fi set -- "$NAME" fi # Make sure that all names are valid chroot names. # For anything but restore, make sure all chroots exist before doing anything. for NAME in "$@"; do if ! validate_name "$NAME"; then error 2 "Invalid chroot name '$NAME'." fi if [ -z "$RESTORE" ]; then CHROOT="$CHROOTS/$NAME" # Check for existence if [ ! -d "$CHROOT" ]; then extra='' if [ "${NAME#-}" != "$NAME" ]; then extra=" Please specify all options before chroot names." fi error 2 "$CHROOT not found.$extra" fi fi done # Display all chroot names if using -a without any other operations. if [ -n "$ALLCHROOTS" -a -z "$BACKUP$DELETE$ENCRYPT$KEYFILE$LISTDETAILS$MOVE$COPY$RESTORE" ]; then echo "$@" exit 0 fi # If TARBALL ends in a slash or we're restoring multiple chroots, make directory if [ -n "$TARBALL" ] && \ [ $# -ge 2 -o -d "$TARBALL" -o "${TARBALL%/}" != "$TARBALL" ]; then TARBALL="${TARBALL%/}/" mkdir -p "$TARBALL" fi # Avoid kernel panics due to slow I/O disablehungtask # Make sure we always exit with echo on the tty. addtrap "stty echo 2>/dev/null" # Prints out a fancy spinner that updates every time a line is fed in, unless # the output is not to a tty, in which case it just prints a new line. # $1: number of lines between each update of the spinner # $2...: Command to be run # Erases the line each time, so it will always be at position 0. # Either expect this and put text later in the line, or give this its own line. spinner() { local spin="$1" shift if [ -t 2 ]; then # Keep track of the exit status of the piped command local ret="`(("$@" || echo "$?" 1>&3) | mawk -Winteractive ' BEGIN { printf "\r" } { y = (y+1) % '"$spin"' if (y == 0) { x = (x+1) % 4 printf substr("\|/-", x+1, 1) "\r" } }' 1>&2) 3>&1`" if [ -n "$ret" ]; then return "$ret" fi else echo 1>&2 "$@" 1>/dev/null fi } # Process each chroot for NAME in "$@"; do # Double check $NAME (better paranoid than sorry) if ! validate_name "$NAME"; then error 2 "Invalid chroot name '$NAME'." fi CHROOT="$CHROOTS/$NAME" # Check for existence and unmount/delete the chroot. if [ -d "$CHROOT" ]; then if [ -n "$LISTDETAILS" ]; then getversion='y' echo "name: $NAME" if [ -f "$CHROOT/.ecryptfs" ]; then if [ ! -d "/var/run/crouton/$CHROOT" \ -a "$LISTDETAILS" -gt 1 ]; then sh "$BINDIR/mount-chroot" -c "$CHROOTS" -- "$NAME" || true fi if mountpoint -q "/var/run/crouton/$CHROOT"; then echo "encrypted: yes, unlocked" else echo "encrypted: yes, locked" getversion='n' fi else echo "encrypted: no" fi if [ "$getversion" = 'y' ]; then CROUTON_NO_UNMOUNT=1 sh "$BINDIR/enter-chroot" \ -c "$CHROOTS" -n "$NAME" -x /usr/local/bin/croutonversion \ || true fi fi if [ "$RESTORE" = 1 ]; then error 2 "$CHROOT already exists! Specify a second -r to overwrite it (dangerous)." elif [ -n "$RESTORE" ]; then EXISTS='y' elif ! sh -e "$BINDIR/unmount-chroot" $YESPARAM \ -c "$CHROOTS" -- "$NAME"; then if [ -n "$NEEDS_UNMOUNT" ]; then exit 1 fi fi elif [ -n "$RESTORE" ]; then EXISTS='' else # This should have been caught earlier error 2 "$CHROOT not found." fi # Delete the chroot? if [ -n "$DELETE" ]; then # Confirm deletion if [ "${YES#[Aa]}" = "$YES" ]; then echo -n "Delete $CHROOT? [a/y/N] " 1>&2 if [ -n "$CROUTON_EDIT_RESPONSE" ]; then YES="$CROUTON_EDIT_RESPONSE" echo "$YES" 1>&2 else read -r YES fi if [ "${YES#[AaYy]}" = "$YES" ]; then error 2 "Aborting deletion of $CHROOT" fi fi # Delete the chroot echo -n " Deleting $CHROOT..." 1>&2 spinner 1000 rm -rvf --one-file-system "$CHROOT" echo "Finished deleting $CHROOT" 1>&2 continue fi # Backup the chroot if [ -n "$BACKUP" ]; then dest="$TARBALL" date="`date '+%Y%m%d-%H%M'`" if [ -z "$dest" -o -d "$TARBALL" ]; then dest="$TARBALL$NAME-$date.tar" # Only compress if it's not encrypted (it'd be a waste of time) if [ ! -f "$CHROOT/.ecryptfs" ]; then dest="$dest.gz" fi fi # If we're writing to a fat32 filesystem, split the file at 4GB chunks if ! df -T "`getmountpoint "$dest"`" | awk '$2~"^v?fat"{exit 1}'; then SPLIT="${SPLIT:-4095}" fi if [ -n "$SPLIT" ]; then tardest="`mktemp -d --tmpdir=/tmp 'crouton-backup.XXX'`" addtrap "rm -rf '$tardest'" tardest="$tardest/pipe.${dest##*/}" mkfifo -m 600 "$tardest" split -b "${SPLIT}m" -a 4 "$tardest" "$dest.part-" & splitpid="$!" else tardest="$dest" splitpid='' fi echo -n " Backing up $CHROOT to $(readlink -m -- "$dest")..." 1>&2 addtrap "echo 'Deleting partial archive.' 1>&2; \ kill '$splitpid' 2>/dev/null; rm -f '$dest' '$dest.part-'*" ret=0 spinner 1 tar --checkpoint=100 --checkpoint-action=exec=echo \ --one-file-system -V "crouton:backup.${date%-*}${date#*-}-$NAME" \ -caf "$tardest" -C "$CHROOTS" "$NAME" || ret="$?" if [ -n "$SPLIT" ]; then wait "$splitpid" || ret="$?" mv -f "$dest.part-aaaa" "$dest" || ret="$?" fi if [ "$ret" -ne 0 ]; then echo "Unable to backup $CHROOT." 1>&2 exit "$ret" fi # Make sure filesystem is sync'ed sync undotrap echo "Finished backing up $CHROOT to $dest" 1>&2 fi # Restore the chroot if [ -n "$RESTORE" ]; then src="$TARBALL" if [ -z "$src" -o -d "$TARBALL" ]; then src='' file="$TARBALL$NAME" # Search for the alphabetically last tarball with src. # Dated tarballs take precedence over undated tarballs. for file in "$file."* "$file-"*; do if [ "${file%.part-[a-z][a-z][a-z][a-z]}" != "$file" \ -o ! -f "$file" ]; then continue fi # Confirm it's a tarball if ! tar --test-label -f "$file" >/dev/null 2>&1; then continue fi # Since * alphabetizes, always keep the last one src="$file" done if [ -z "$src" ]; then error 2 "Unable to find a tarball for $NAME. You can specify one with -f." fi echo "Found $src for restoring $NAME." 1>&2 elif ! tar --test-label -f "$src" >/dev/null 2>&1; then error 2 "$src doesn't appear to be a valid tarball." fi if [ -n "$EXISTS" ]; then echo "WARNING: $CHROOT already exists. Deleting it before restoring." 1>&2 echo "Press Control-C to abort; restoration will continue in 5 seconds." 1>&2 sleep 5 sh -e "$BINDIR/edit-chroot" -d -y -c "$CHROOTS" "$NAME" fi echo -n " Restoring $src to $CHROOT..." 1>&2 mkdir -p "$CHROOT" if [ -f "$src.part-aaab" ]; then # Detect the type of compression before sending it through a fifo for tarparam in -z -j -J -Z --no-auto-compress fail; do if [ "$tarparam" = 'fail' ]; then error 2 "Unable to detect compression method of $src" elif tar $tarparam --test-label -f "$src" >/dev/null 2>&1; then break fi done # Don't let tar get tripped up by cat's incomplete writes tarparam="$tarparam -B" tarsrc="`mktemp -d --tmpdir=/tmp 'crouton-restore.XXX'`" addtrap "rm -rf '$tarsrc'" tarsrc="$tarsrc/pipe" mkfifo -m 600 "$tarsrc" cat "$src" "$src.part"* >> "$tarsrc" & catpid="$!" addtrap "kill '$catpid' 2>/dev/null" else tarsrc="$src" tarparam='-a' catpid='' fi spinner 1 tar --checkpoint=200 --checkpoint-action=exec=echo $tarparam \ --one-file-system -xf "$tarsrc" -C "$CHROOT" --strip-components=1 if [ -n "$catpid" ]; then wait "$catpid" fi # Make sure filesystem is sync'ed sync echo "Finished restoring $src to $CHROOT" 1>&2 fi # Update the keyfile if [ -n "$KEYFILE" ]; then newkeyfile="$KEYFILE" # Find the current keyfile oldkeyfile="$CHROOT/.ecryptfs" if [ -f "$oldkeyfile" ]; then header="`head -n1 "$oldkeyfile"`" if [ -n "$header" ]; then oldkeyfile="$header" fi fi if [ "$newkeyfile" = '-' ]; then newkeyfile="$CHROOT/.ecryptfs" fi if [ "${newkeyfile#/}" = "$newkeyfile" ]; then newkeyfile="$PWD/$newkeyfile" fi if [ -d "$newkeyfile" -o "${newkeyfile%/}" != "$newkeyfile" ]; then newkeyfile="${newkeyfile%/}/$NAME" fi oldkeyfile="`readlink -m -- "$oldkeyfile"`" keyfilecanon="`readlink -m -- "$newkeyfile"`" if [ ! -f "$oldkeyfile" ]; then # If there is no old keyfile, make sure we've requested encryption. if [ -z "$ENCRYPT" ]; then error 1 "Old key file not found" fi elif [ "$oldkeyfile" != "$keyfilecanon" ]; then chrootecryptfsfile="`readlink -m -- "$CHROOT/.ecryptfs"`" # Don't clobber a file already there if [ -e "$newkeyfile" -a "$newkeyfile" != "$chrootecryptfsfile" ]; then error 1 "Encryption key file $newkeyfile already exists. Refusing to overwrite!" fi # Write the new keyfile before removing the old. if ! mkdir -p "`dirname "$newkeyfile"`"; then error 1 "Unable to create directory for $newkeyfile" fi echo "Moving key file from $oldkeyfile to $newkeyfile" 1>&2 (echo; tail -n+2 "$oldkeyfile") > "$newkeyfile" # Remove old keyfile before writing .ecryptfs, as it may be the same rm -f "$oldkeyfile" if [ "$chrootecryptfsfile" != "$newkeyfile" ]; then echo "$newkeyfile" > "$CHROOT/.ecryptfs" fi elif [ -z "$ENCRYPT$MOVE" ]; then echo "Keyfile is already located at $newkeyfile" 1>&2 fi fi # Encrypt/rewrap the chroot if [ -n "$ENCRYPT" ]; then # Use mount-chroot to do the heavy lifting unmount="sh -e '$BINDIR/unmount-chroot' -y -c '$CHROOTS' -- '$NAME'" addtrap "$unmount" if [ -n "$KEYFILE" ]; then sh -e "$BINDIR/mount-chroot" -ee -k "$KEYFILE" -c "$CHROOTS" -- "$NAME" else sh -e "$BINDIR/mount-chroot" -ee -c "$CHROOTS" -- "$NAME" fi undotrap eval "$unmount" fi # Move the chroot if requested if [ -n "$MOVE" ]; then target="$MOVE" if [ "${target##*/}" = "$target" ]; then # No slashes in the path. Assume renaming. if ! validate_name "$target"; then error 2 "Invalid target chroot name '$target'." fi target="$CHROOTS/$target" elif [ "${target%/}" != "$target" ]; then # Ends in a slash; append name. target="$target$NAME" fi if [ -e "$target" ]; then # Can't tell if the destination is a directory or a chroot that # already exists; be safe and assume it was a mistake. error 2 "$target already exists" fi # Check if we're changing filesystems, because we should cp+rm for # safety. We don't do this when encrypting a chroot (see mount-chroot), # because that would require 2x the space on one device. When switching # filesystems like this, however, that isn't a concern. if [ "`getmountpoint "$target"`" != "`getmountpoint "$CHROOT"`" ]; then echo "Moving $CHROOT across filesystems to $target" 1>&2 echo 'This will take a while.' 1>&2 echo "If the operation gets interrupted, you can safely delete $target" 1>&2 # Confirm long operation if [ "${YES#[Aa]}" = "$YES" ]; then echo -n "Are you sure you want to continue? [a/y/N] " 1>&2 if [ -n "$CROUTON_EDIT_RESPONSE" ]; then YES="$CROUTON_EDIT_RESPONSE" echo "$YES" 1>&2 else read -r YES fi if [ "${YES#[AaYy]}" = "$YES" ]; then error 2 "Aborting move of $CHROOT" fi fi if ! mkdir -p "`dirname "$target"`"; then error 1 "Unable to create directory for $target" fi echo -n " Copying to $target..." 1>&2 spinner 200 cp -av --one-file-system "$CHROOT" "$target" echo "Finished copying to $target" 1>&2 echo -n " Deleting old $CHROOT..." 1>&2 spinner 1000 rm -rfv --one-file-system "$CHROOT" echo "Finished deleting old $CHROOT" 1>&2 else if ! mkdir -p "`dirname "$target"`"; then error 1 "Unable to create directory for $target" fi echo "Moving $CHROOT to $target" 1>&2 mv "$CHROOT" "$target" fi fi # Copy the chroot if requested if [ -n "$COPY" ]; then target="$COPY" if [ "${target##*/}" = "$target" ]; then # No slashes in the path. Assume same directory. if ! validate_name "$target"; then error 2 "Invalid target chroot name '$target'." fi target="$CHROOTS/$target" elif [ "${target%/}" != "$target" ]; then # Ends in a slash; append name. target="$target$NAME" fi if [ -e "$target" ]; then # Can't tell if the destination is a directory or a chroot that # already exists; be safe and assume it was a mistake. error 2 "$target already exists" fi if ! mkdir -p "`dirname "$target"`"; then error 1 "Unable to create directory for $target" fi echo "Copying $CHROOT to $target" 1>&2 spinner 200 cp -av --one-file-system "$CHROOT" "$target" fi done exit 0 ================================================ FILE: host-bin/enter-chroot ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" BACKGROUND='' BINDIR="`dirname "\`readlink -f -- "$0"\`"`" CHROOTS="`readlink -m -- "$BINDIR/../chroots"`" KEYFILE='' LOGIN='' NAME='' TARGET='' USERNAME='1000' TMPXMETHOD='' NOLOGIN='' SETUPSCRIPT='/prepare.sh' USAGE="$APPLICATION [options] [command [args...]] Enters an installed Debian-based chroot for running alongside Chromium OS. By default, it will log into the primary user on the first chroot found. You can specify a command and parameters to run instead of an interactive shell. Options: -b Fork and run the specified command silently in the background. -c CHROOTS Directory the chroots are in. Default: $CHROOTS -l Make the command part of a login. Parameters are passed directly to the chroot command, and a call to su is appended. -k KEYFILE Override the auto-detected encryption key location. -n NAME Name of the chroot to enter. Default: first one found in CHROOTS -t TARGET Only enter the chroot if it contains the specified TARGET. -u USERNAME Username (or UID) to log into. Default: 1000 (the primary user) -X XMETHOD Override the auto-detected XMETHOD for this session. -x Does not log in, but directly executes the command instead. Note that the environment will be empty (sans TERM). Specify -x a second time to run the $SETUPSCRIPT script." # Common functions . "$BINDIR/../installer/functions" # Safely launch a command ($*) via /bin/sh within the chroot as the root user. chrootcmd() { # env may be overridden when running in the background; don't let it fork. local ret=0 oldtrap="$TRAP" TRAP='' env -i chroot "$CHROOT" su -s '/bin/sh' -c "$*" - root || ret=$? local pid="$!" # $pid might not be set if env has not been redefined yet if [ -n "$BACKGROUND" ] && [ -n "$pid" ]; then wait "$pid" || ret=$? fi TRAP="$oldtrap" return "$ret" } # Process arguments prevoptind=1 while getopts 'bc:k:ln:t:u:X:x' f; do # Disallow empty string as option argument if [ "$((OPTIND-prevoptind))" = 2 -a -z "$OPTARG" ]; then error 2 "$USAGE" fi prevoptind="$OPTIND" case "$f" in b) BACKGROUND='y';; c) CHROOTS="`readlink -m -- "$OPTARG"`";; k) KEYFILE="$OPTARG";; l) LOGIN='y';; n) NAME="$OPTARG";; t) TARGET="$OPTARG";; u) USERNAME="$OPTARG";; X) TMPXMETHOD="$OPTARG";; x) NOLOGIN="$((NOLOGIN+1))" [ "$NOLOGIN" -gt 2 ] && NOLOGIN=2;; \?) error 2 "$USAGE";; esac done shift "$((OPTIND-1))" # Shift away empty string as first argument (used in start* scripts to mark # the end of user-specified parameters) if [ "$#" -ge 1 -a -z "$1" ]; then shift fi # We need to run as root if [ "$USER" != root -a "$UID" != 0 ]; then error 2 "$APPLICATION must be run as root." fi # We need a command if we specified to run in the background if [ -n "$BACKGROUND" -a $# = 0 ]; then error 2 "A command must be specified in order to run in the background." fi # If -x is specified twice, our command is the setup script. if [ "$NOLOGIN" = 2 ]; then if [ $# != 0 ]; then error 2 "A command cannot be specified with -xx." fi if [ -n "$BACKGROUND" ]; then error 2 "Cannot run the setup script in the background." fi set -- "$SETUPSCRIPT" fi # Select the first chroot available if one hasn't been specified if [ -z "$NAME" ]; then haschroots='' for CHROOT in "$CHROOTS"/*; do if [ ! -d "$CHROOT/etc" -a ! -f "$CHROOT/.ecryptfs" ]; then continue fi haschroots='y' if [ -n "$TARGET" ]; then if ! grep -q "^$TARGET$" "$CHROOT/.crouton-targets" 2>/dev/null; then continue fi fi NAME="${CHROOT##*/}" break done if [ -z "$haschroots" ]; then error 1 "No chroots found in $CHROOTS" fi if [ -z "$NAME" ]; then error 1 "No chroots with target '$TARGET' found in $CHROOTS" fi elif [ -n "$TARGET" ]; then if ! grep -q "^$TARGET$" "$CHROOTS/$NAME/.crouton-targets" 2>/dev/null; then error 1 "$CHROOTS/$NAME does not contain target '$TARGET'" fi fi # Check to ensure that the XMETHOD requested has been installed if [ -n "$TMPXMETHOD" ]; then if ! grep -q "^${TMPXMETHOD%%-*}$" "$CHROOTS/$NAME/.crouton-targets"; then error 1 "$CHROOTS/$NAME does not contain XMETHOD '${TMPXMETHOD%%-*}'" fi fi # Avoid kernel panics due to slow I/O disablehungtask # Allow X server running as normal user to set/drop DRM master drm_relax_file="/sys/kernel/debug/dri/drm_master_relax" if [ -f "$drm_relax_file" ]; then echo 'Y' > "$drm_relax_file" fi # Make sure we always exit with echo on the tty. addtrap "stty echo 2>/dev/null" # Mount the chroot and update our CHROOT path CHROOTSRC="$CHROOTS/$NAME" if [ -n "$KEYFILE" ]; then CHROOT="`sh -e "$BINDIR/mount-chroot" \ -k "$KEYFILE" -p -c "$CHROOTS" -- "$NAME"`" else CHROOT="`sh -e "$BINDIR/mount-chroot" -p -c "$CHROOTS" -- "$NAME"`" fi if [ "$NOLOGIN" != 2 ]; then echo "Entering $CHROOTSRC..." 1>&2 fi # In disk full situations, mount-chroot can be empty. Good time to check sanity. if [ -z "$CHROOT" ]; then error 1 'Something is wrong with the crouton install. Please make sure you have sufficient space available, then re-install the chroot and try again.' fi # Register the crash_reporter_wrapper to properly handle coredumps if [ -f "$BINDIR/crash_reporter_wrapper" ]; then if ! sh -e "$BINDIR/crash_reporter_wrapper" register; then echo 'WARNING: Unable to register core dump handler.' 1>&2 fi fi if [ -z "$CROUTON_NO_UNMOUNT" ]; then # Auto-unmount everything below and including the chroot upon exit addtrap "sh -e '$BINDIR/unmount-chroot' -yc '$CHROOTS' -- '$NAME'" fi # If our root is on an external disk we need to ensure USB device persistence is # enabled otherwise we will lose the file-system after a suspend event. if [ "${CHROOTSRC#/media}" != "$CHROOTSRC" ]; then for usbp in /sys/bus/usb/devices/*/power/persist; do if [ -e "$usbp" ]; then echo 1 > "$usbp" fi done fi # Offer to run the setup script if it exists and yet we're logging in. if [ -z "$NOLOGIN" -a -f "$CHROOT$SETUPSCRIPT" ]; then echo 'A chroot setup script still exists inside the chroot.' 1>&2 echo 'The chroot may not be fully set up.' 1>&2 response='' # A finished setup script will have permissions 500 if [ "`stat -c '%a' "$CHROOT$SETUPSCRIPT"`" != '500' ]; then echo 'However, it appears the setup script is invalid.' 1>&2 response='d' fi if [ -t 0 -a -z "$response" ]; then echo -n 'Would you like to finish the setup? [Y/n/d] ' 1>&2 read -r response fi if [ -z "$response" -o "${response#[Yy]}" != "$response" ]; then echo 'Preparing chroot environment...' 1>&2 if CROUTON_NO_UNMOUNT=1 sh -e "$BINDIR/enter-chroot" \ -c "$CHROOTS" -n "$NAME" -xx; then echo 'Setup completed. Entering chroot...' 1>&2 response='' else echo 'The chroot setup script may be broken. Your chroot is not fully configured.' 1>&2 response='d' fi fi if [ "${response#[Dd]}" != "$response" ]; then echo 'Removing the chroot setup script. You may want to update your chroot again.' 1>&2 rm -f "$CHROOT$SETUPSCRIPT" elif [ -n "$response" ]; then echo 'Skipping setup. You will be prompted again next time.' 1>&2 fi fi # Resolve USERNAME if it is a UID (and we're logging in) passwd="$CHROOT/etc/passwd" if [ -z "$NOLOGIN" ]; then if [ ! -r "$passwd" ]; then error 1 "$CHROOTSRC doesn't appear to be a valid chroot." fi case "$USERNAME" in ''|*[!0-9]*) # Make sure the username exists if ! grep -q "^$USERNAME:" "$passwd"; then error 1 "User $USERNAME not found in $NAME" fi;; *) # Resolve the UID uid="$USERNAME" USERNAME="`awk -F: '$3=='"$uid"'{print $1; exit}' "$passwd"`" if [ -z "$USERNAME" ]; then error 1 "UID $uid not found in $NAME" fi esac # Detect the home directory and shell for the user CHROOTHOME="`awk -F: '$1=="'"$USERNAME"'"{print $6; exit}' "$passwd"`" CHROOTSHELL="`awk -F: '$1=="'"$USERNAME"'"{print $NF; exit}' "$passwd"`" else CHROOTSHELL='/bin/sh' fi # Save the chroot name to the chroot echo "$NAME" > "$CHROOT/etc/crouton/name" # Ensure $CHROOT/var/host exists. mkdir -p "$CHROOT/var/host" # Copy in the current Chromium OS version for reference cp -f '/etc/lsb-release' "$CHROOT/var/host/" # Copy CRAS version into the chroot cras_test_client --version 2>/dev/null > "$CHROOT/var/host/cras-version" || true # Copy the latest Xauthority into the chroot if [ -f "${XAUTHORITY:=/home/chronos/.Xauthority}" ]; then cp -f "$XAUTHORITY" "$CHROOT/var/host/Xauthority" chmod 444 "$CHROOT/var/host/Xauthority" # Be backwards-compatible, just in case if [ -f "$CHROOT/etc/X11/host-Xauthority" ]; then ln -sfT '/var/host/Xauthority' "$CHROOT/etc/X11/host-Xauthority" fi fi # Prepare chroot filesystem # Soft-link resolv.conf so that updates are automatically propagated ln -sfT '/var/host/shill/resolv.conf' "$CHROOT/etc/resolv.conf" # Sanity check of the timezone setting localtime="$CHROOT/etc/localtime" hostlocaltime='/var/host/timezone/localtime' if [ -h "$localtime" ] && [ "`readlink -- "$localtime"`" = "$hostlocaltime" ]; then timezone="`readlink -m -- /var/lib/timezone/localtime || true`" if [ -z "$timezone" -o ! -e "$CHROOT$LOCALTIME" ]; then echo "\ WARNING: the timezone selected in Chromium OS does not exist inside the chroot. To set the chroot's timezone, run the following: sudo dpkg-reconfigure tzdata" 1>&2 else # Set /etc/timezone in chroot - fixes the clock in Unity echo "${timezone#/usr/share/zoneinfo/}" > "$CHROOT/etc/timezone" fi fi # Follows and fixes dangerous symlinks, returning the canonicalized path. fixabslinks() { local p="$CHROOT/$1" c # Follow and fix dangerous absolute symlinks while c="`readlink -m -- "$p"`" && [ "$c" != "$p" ]; do p="$CHROOT${c#"$CHROOT"}" done echo "$p" } # Bind-mounts $1 into $CHROOT/${2:-"$1"} if $2 is not already mounted # If $3 is specified, remounts with the specified options. # If $1 starts with a -, it's considered options to the bind mount, and the rest # of the parameters are shifted. bindmount() { bindopts='' if [ "${1#"-"}" != "$1" ]; then bindopts="$1" shift fi local target="`fixabslinks "${2:-"$1"}"`" if mountpoint -q "$target"; then return 0 fi mkdir -p "$target" mount --bind $bindopts "$1" "$target" mount -i -o 'remount,symfollow' "$target" 2>/dev/null || true if [ -n "$3" ]; then mount -i -o "remount,$3" "$target" fi } # Creates a tmpfs mount at $CHROOT/$1 with options $2 if not already mounted tmpfsmount() { local target="`fixabslinks "$1"`" if mountpoint -q "$target"; then return 0 fi mkdir -p "$target" mount -i -t tmpfs -o "rw${2:+,}$2" tmpfs "$target" mount -i -o 'remount,symfollow' "$target" 2>/dev/null || true } # If /var/run isn't mounted, we know the chroot hasn't been started yet. if mountpoint -q "`fixabslinks '/var/run'`"; then firstrun='' else firstrun='y' fi bindmount /dev bindmount /dev/pts bindmount /dev/shm bindmount /tmp /tmp exec bindmount /proc tmpfsmount /var/run 'noexec,nosuid,mode=0755,size=10%' tmpfsmount /var/run/lock 'noexec,nosuid,nodev,size=5120k' bindmount /var/run/dbus /var/host/dbus bindmount /var/run/shill /var/host/shill bindmount /var/lib/timezone /var/host/timezone for m in /lib/modules/*; do if [ -d "$m" ]; then bindmount '-o ro' "$m" fi done # Add a shm symlink to our new /var/run ln -sfT /dev/shm "`fixabslinks '/var/run'`/shm" # Setup udev control directory in the chroot # Chromium OS >=6092 uses /run/udev, older versions /dev/.udev if [ -d /var/run/udev ]; then bindmount /var/run/udev /var/host/udev ln -sfT /var/host/udev "`fixabslinks '/var/run'`/udev" else # Add a /run/udev symlink for later versions of udev in chroot ln -sfT /dev/.udev "`fixabslinks '/var/run'`/udev" fi if [ -d /var/run/cras ]; then bindmount /var/run/cras /var/host/cras # Add a /var/host/cras symlink for CRAS clients ln -sfT /var/host/cras "$(fixabslinks '/var/run')/cras" else echo "\ WARNING: CRAS not running in Chromium OS. Audio forwarding will not work." 1>&2 fi if [ -d /var/run/chrome ]; then bindmount /var/run/chrome /var/host/chrome # Add a /var/host/chrome symlink for display clients ln -sfT /var/host/chrome "$(fixabslinks '/var/run')/chrome" else echo "\ WARNING: Chrome not running in Chromium OS. Display forwarding will not work." 1>&2 fi # Bind-mount /media, specifically the removable directory destmedia="`fixabslinks '/var/host/media'`" if ! mountpoint -q "$destmedia"; then mount --make-shared /media mkdir -p "$destmedia" "$CHROOT/media" ln -sfT "/var/host/media/removable" "$CHROOT/media/removable" mount --rbind /media "$destmedia" fi # Provide a default /etc/crouton/shares file shares="`fixabslinks '/etc/crouton/shares'`" if [ -e "$shares" -a ! -f "$shares" ]; then echo "Not mounting shares: /etc/crouton/shares is not a file." 1>&2 elif [ ! -f "$shares" ]; then cat > "$shares" <&2 continue fi src="$localmyfiles/${src#*/}";; download|downloads) if [ -z "$localdownloads" ]; then echo "Not mounting share (no Chromium OS user):$line" 1>&2 continue fi src="$localdownloads/${src#*/}";; encrypt|encrypted) if [ -z "$localencrypted" ]; then echo "Not mounting share (no Chromium OS user):$line" 1>&2 continue fi src="$localencrypted/${src#*/}";; share|shares|shared) src="$localshare/${src#*/}";; error) # Print the error message from awk script. echo "$dest" 1>&2 echo "$opts" 1>&2 continue;; *) echo "Invalid share:$line" 1>&2 continue;; esac # Expand dest for homedirs if [ "${dest#"~"}" != "$dest" ]; then destuser="${dest%%/*}" if [ "$destuser" = '~' ]; then if [ -z "$CHROOTHOME" ]; then echo "Not mounting share (no chroot user):$line" 1>&2 continue fi dest="$CHROOTHOME/${dest#*/}" else dest="/home/${destuser#"~"}/${dest#*/}" fi fi # Do the bindmount mkdir -m 700 -p "$src" if ! bindmount "$src" "$dest" "${opts:-exec}"; then echo "Failed to mount share:$line" 1>&2 fi done fi # Bind-mount /sys recursively, making it a slave in the chroot if ! mountpoint -q "$CHROOT/sys"; then mkdir -p "$CHROOT/sys" mount --make-rshared /sys mount --rbind /sys "$CHROOT/sys" mount --make-rslave "$CHROOT/sys" # Unmount selinux in the chroot, make a fake entry to set enforce=0 if mountpoint -q "$CHROOT/sys/fs/selinux"; then umount "$CHROOT/sys/fs/selinux" mount -t tmpfs none "$CHROOT/sys/fs/selinux" echo 0 > "$CHROOT/sys/fs/selinux/enforce" mount -o remount,ro "$CHROOT/sys/fs/selinux" fi fi # Modify chroot's /sys/class/drm and /dev/dri to avoid vgem/mfgsys varrundrm="$(fixabslinks '/var/run/drm')" varrundri="$(fixabslinks '/var/run/dri')" sysclassdrm="$(fixabslinks '/sys/class/drm')" devdri="$(fixabslinks '/dev/dri')" if [ ! -d "$varrundrm" -a -d "$sysclassdrm" -a -d "$devdri" ]; then cp -Ta "$sysclassdrm" "$varrundrm" cp -Ta "$devdri" "$varrundri" for f in "$varrundrm"/*; do if [ -h "$f" ] && readlink -- "$f" | grep -qF -e /vgem/ -e mfgsys; then rm -f "$f" "$varrundri/${f##*/}" fi done # Scanning of /dev/dri is done sequentially, so make sure there's a card0 for f in "$varrundri/card"*; do [ -e "$varrundri/card0" ] || mv -f "$f" "$varrundri/card0" done mount --bind "$varrundrm" "$sysclassdrm" mount --bind "$varrundri" "$devdri" fi # Get croutonversion variables croutonversion="$CHROOT/usr/local/bin/croutonversion" CHROOTRELEASE="unknown" if [ -x "$croutonversion" ]; then CHROOTRELEASE="`"$croutonversion" -r 2>/dev/null || echo "$CHROOTRELEASE"`" fi # For test machines with low entropy, bind mount /dev/urandom to /dev/random if [ -n "$CROUTON_WEAK_RANDOM" ]; then mount --bind "$CHROOT/dev/urandom" "$CHROOT/dev/random" fi # Fix group numbers for critical groups to match Chromium OS. This is necessary # so that users have access to shared hardware, such as video and audio. gfile="$CHROOT/etc/group" if [ -f "$gfile" ]; then for group in audio:hwaudio cras:audio cdrom chronos-access:crouton \ devbroker-access dialout disk floppy i2c input lp serial \ tape tty usb:plugdev uucp video wayland; do hostgroup="${group%:*}" chrootgroup="${group#*:}" gid="$(awk -F: '$1=="'"$hostgroup"'"{print $3; exit}' '/etc/group')" curgid="$(awk -F: '$1=="'"$chrootgroup"'"{print $3; exit}' "$gfile")" if [ -z "$gid" ]; then if [ -z "$curgid" ]; then echo "Creating unassociated $chrootgroup group..." 1>&2 chrootcmd groupadd --system "$chrootgroup" fi continue fi if [ "$gid" = "$curgid" ]; then continue elif [ -z "$curgid" ]; then echo "Creating $chrootgroup group with GID $gid..." 1>&2 groupcmd=groupadd else echo "Changing $chrootgroup GID from $curgid to $gid..." 1>&2 groupcmd=groupmod fi move="`awk -F: '$3=='"$gid"'{print $1; exit}' "$gfile"`" if [ -n "$move" ]; then ngid="$gid" while grep -q ":$ngid:" "$gfile"; do ngid="$((ngid+1))" done echo "Moving $move GID from $gid to $ngid..." 1>&2 chrootcmd groupmod -g "$ngid" "$move" fi chrootcmd "$groupcmd" -g "$gid" "$chrootgroup" done fi # To run silently, we override the env command to launch a background process, # and move the trap code to happen there. if [ -n "$BACKGROUND" ]; then env() { # Shuffle FDs around to preserve stdin { ( trap '' INT HUP trap "$TRAP" 0 exec 0<&9 9<&- [ -t 0 ] && exec < /dev/null [ -t 1 ] && exec > /dev/null [ -t 2 ] && exec 2>&1 /usr/bin/env "$@" ) & } 9<&0 } fi ret=0 # Launch the system dbus unless we are entering a basic shell. if [ "$NOLOGIN" != 1 ] && grep -q '^root:' "$passwd" 2>/dev/null; then # Try to detect the dbus user by parsing its configuration file # If it fails, or if the user does not exist, `id -un '$dbususer'` # will fail, and we fallback on a default user name ("messagebus") dbususer="`echo "cat /busconfig/user/text()" \ | xmllint --shell "$CHROOT/etc/dbus-1/system.conf" 2>/dev/null \ | grep '^[a-z][-a-z0-9_]*$' || true`" chrootcmd " if ! hash dbus-daemon 2>/dev/null; then exit 0 fi dbususer='$dbususer'"' pidfile="/var/run/dbus/pid" if [ -f "$pidfile" ]; then if grep -q "^dbus-daemon" "/proc/`cat "$pidfile"`/cmdline" \ 2>/dev/null; then exit 0 fi rm -f "$pidfile" fi mkdir -p /var/run/dbus dbususer="`id -un "$dbususer" 2>/dev/null || echo "messagebus"`" dbusgrp="`id -gn "$dbususer" 2>/dev/null || echo "messagebus"`" chown "$dbususer:$dbusgrp" /var/run/dbus exec dbus-daemon --system --fork' || ret=$? if [ "$ret" != 0 ]; then echo "WARNING: starting chroot system dbus daemon failed with code $ret" 1>&2 ret=0 fi # Launch systemd-logind if available and not already running # Whitelisted for saucy and trusty systemd_dir="`fixabslinks '/run/systemd'`" if [ -x "$CHROOT/lib/systemd/systemd-logind" ] && \ [ ! -d "$systemd_dir" ] && \ [ "$CHROOTRELEASE" = 'saucy' -o "$CHROOTRELEASE" = 'trusty' ]; then # Every piece of systemd code ever assumes that this directory exists mkdir -p "$systemd_dir" # Create systemd cgroup if necessary if ! mountpoint -q "$CHROOT/sys/fs/cgroup/systemd"; then mkdir -p "$CHROOT/sys/fs/cgroup/systemd" mount -t cgroup -o nosuid,noexec,nodev,none,name=systemd systemd \ "$CHROOT/sys/fs/cgroup/systemd" fi # systemd-logind doesn't fork chrootcmd "/lib/systemd/systemd-logind >/dev/null 2>&1 /dev/null 2>&1 &1 ) & noticepid="$!" addtrap "kill '$noticepid' 2>/dev/null" chrootcmd 'exec /etc/rc.local >/dev/null 2>/dev/null /dev/null || true wait "$noticepid" || true if [ "$ret" != 0 ]; then echo "WARNING: /etc/rc.local failed with code $ret" 1>&2 ret=0 fi fi if [ $# = 0 -o -n "$LOGIN" ]; then env -i TERM="$TERM" chroot "$CHROOT" "$@" su - "$USERNAME" || ret=$? else # Escape out the command cmd="export SHELL='$CHROOTSHELL';" if [ -n "$TMPXMETHOD" ]; then cmd="$cmd export XMETHOD='$TMPXMETHOD';" fi for param in "$@"; do cmd="$cmd'`echo -n "$param" | sed "s/'/'\\\\\\''/g"`' " done env -i TERM="$TERM" chroot "$CHROOT" \ su -s '/bin/sh' -c "$cmd" - "$USERNAME" \ || ret=$? fi fi # We don't want to trap for this proccess if we're running in the background if [ -n "$BACKGROUND" ]; then settrap '' fi # Cleanup all happens in the exit trap exit $ret ================================================ FILE: host-bin/mount-chroot ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" BINDIR="`dirname "\`readlink -f -- "$0"\`"`" CHROOTS="`readlink -m -- "$BINDIR/../chroots"`" CREATE='' ENCRYPT='' KEYFILE='' PRINT='' ROOT="`readlink -m -- '/var/run/crouton'`" MOUNTOPTS='rw,dev,exec,suid' METRICSDIR='/run/metrics/external/crouton' USAGE="$APPLICATION [options] name [...] Mounts one or more chroots into a root-only subdirectory of $ROOT Options: -c CHROOTS Directory the chroots are in. Default: $CHROOTS -e If the chroot is not encrypted, encrypt it. If specified twice, prompt to change the encryption passphrase. -k KEYFILE File or directory to store the (encrypted) encryption keys in. If unspecified, the keys will be stored in the chroot if doing a first encryption, or auto-detected on existing chroots. -n Create the chroot if it doesn't exist. -p Prints out the path to the mounted directory on stdout." # Common functions . "$BINDIR/../installer/functions" # Process arguments getopts_string='c:ek:np' while getopts_nextarg; do case "$getopts_var" in c) CHROOTS="`readlink -m -- "$getopts_arg"`";; e) ENCRYPT="$((ENCRYPT+1))";; k) KEYFILE="$getopts_arg";; n) CREATE='y';; p) PRINT='y';; \?) error 2 "$USAGE";; esac done # Need at least one chroot listed if [ $# = 0 ]; then error 2 "$USAGE" fi # We need to run as root if [ "$USER" != root -a "$UID" != 0 ]; then error 2 "$APPLICATION must be run as root." fi # Make sure we always exit with echo on the tty. addtrap "stty echo 2>/dev/null" # Whitelists a directory for symlink and other hardening whitelist() { # As of 67, symlinks and fifos are blocked in stateful partitions. # Add exceptions for this chroot. local sec='/sys/kernel/security' mounted='' if mountpoint -q "$sec"; then mounted=y elif ! mount -n -t securityfs -o nodev,noexec,nosuid securityfs "$sec"; then return fi # Ensure it's mounted rw if ! mount -o remount,rw "$sec"; then echo "Failed to make inode security policies writeable" >&2 return 1 fi policies="$sec/chromiumos/inode_security_policies" if [ -d "$policies" ]; then # Touch allow_symlink first to avoid kernel crash on chromeos-5.15 R126. printf "$CHROOT" > "$policies/allow_symlink" printf "$CHROOT" > "$policies/allow_fifo" fi if [ -z "$mounted" ]; then umount "$sec" fi } # Function to prompt the user for a passphrase. Sets $passphrase. promptNewPassphrase() { echo_tty -n "Choose an encryption passphrase for $NAME: " [ -t 0 ] && stty -echo while [ -z "$passphrase" ]; do read -r passphrase if [ -z "$passphrase" ]; then echo_tty '' echo_tty -n 'You must specify a passphrase: ' continue fi echo_tty '' echo_tty -n 'Please confirm your passphrase: ' read -r confirmation if [ "$confirmation" != "$passphrase" ]; then passphrase='' echo_tty '' echo_tty -n 'Passphrases do not match; try again: ' fi confirmation='' done [ -t 0 ] && stty echo echo_tty '' } # Mount each chroot for NAME in "$@"; do if ! validate_name "$NAME"; then error 2 "Invalid chroot name '$NAME'." fi # Check for existence CHROOT="$CHROOTS/$NAME" movesrc='' if [ -d "$CHROOT" ]; then if [ -f "$CHROOT/.ecryptfs" -o -n "$ENCRYPT" ]; then if [ -z "$ENCRYPT" ]; then ENCRYPT=0 fi # Check for non-encrypted files that we may need to move for file in "$CHROOT/"*; do if [ "${file#*/ECRYPTFS_FNEK_ENCRYPTED}" = "$file" ]; then movesrc="$CHROOT" break fi done fi elif [ -z "$CREATE" ]; then error 1 "$CHROOT not found." else mkdir -p "$CHROOT" fi CHROOTSRC="$CHROOT" CHROOT="$ROOT/${CHROOT#/}" # Ensure there's a root-only folder for the bind-mounted chroot mkdir -p -m 0700 "$CHROOT" chown root:root "$ROOT" chmod 700 "$ROOT" # Extraordinarily vague usage stat; see https://crbug.com/989219 if [ -z "${CROUTON_DISABLE_STATS-}" -a -d "${METRICSDIR%/*}" ]; then mkdir -p -m 0777 "$METRICSDIR" chown root:root "$METRICSDIR" chmod 777 "$METRICSDIR" touch "$METRICSDIR/crouton-started" fi || true if [ -n "$PRINT" ]; then echo "$CHROOT" fi # Check if we actually need to mount if ! mountpoint -q "$CHROOT"; then if [ -z "$ENCRYPT" ]; then mount --bind "$CHROOTSRC" "$CHROOT" mount -i -o "remount,$MOUNTOPTS" "$CHROOT" mount -i -o 'remount,symfollow' "$CHROOT" 2>/dev/null || true mount --make-private "$CHROOT" whitelist "$CHROOT" continue fi # We must be on a terminal, unless we already have a password in env. if [ ! -t 0 -a -z "$CROUTON_PASSPHRASE$CROUTON_NEW_PASSPHRASE" ]; then error 2 'STDIN is not a terminal; cannot request passwords.' fi # Ensure that there's a root password set before decrypting the chroot, # unless the passphrase was specified via env, which isn't secure anyway if [ ! -f '/mnt/stateful_partition/etc/devmode.passwd' ]; then echo_tty \ 'You must have a root password in Chromium OS to mount encrypted chroots.' if [ -z "$CROUTON_PASSPHRASE$CROUTON_NEW_PASSPHRASE" ]; then while ! chromeos-setdevpasswd; do :; done fi fi # Detect the key file if [ -z "$KEYFILE" ]; then KEYFILE="$CHROOTSRC/.ecryptfs" if [ -f "$KEYFILE" ]; then header="`head -n1 "$KEYFILE"`" if [ -n "$header" ]; then KEYFILE="$header" fi fi elif [ "${KEYFILE#/}" = "$KEYFILE" ]; then KEYFILE="$PWD/$KEYFILE" fi if [ -d "$KEYFILE" -o "${KEYFILE%/}" != "$KEYFILE" ]; then KEYFILE="${KEYFILE%/}/$NAME" fi if ! mkdir -p "`dirname "$KEYFILE"`"; then error 1 "Unable to create directory for $KEYFILE" fi # If we just created it, choose and create the keyfile. passphrase="${CROUTON_PASSPHRASE:-"$CROUTON_NEW_PASSPHRASE"}" if [ ! -f "$CHROOTSRC/.ecryptfs" ]; then if [ -e "$KEYFILE" ]; then error 1 "Encryption key file $KEYFILE already exists. Refusing to overwrite!" fi promptNewPassphrase if [ -z "$CROUTON_WEAK_RANDOM" ]; then random="/dev/random" echo 'Generating keys (move the mouse to generate entropy)...' 1>&2 else random="/dev/urandom" echo 'Generating keys from /dev/urandom...' 1>&2 fi key="`hexdump -v -n32 -e'32/1 "%02x"' "$random"`" fnek="`hexdump -v -n32 -e'32/1 "%02x"' "$random"`" echo 'done' 1>&2 # Create key file wrappedkey="`mktemp`" wrappedfnek="`mktemp`" addtrap "rm -f '$wrappedkey' '$wrappedfnek'" echo -n "$key $passphrase" | ecryptfs-wrap-passphrase "$wrappedkey" - echo -n "$fnek $passphrase" | ecryptfs-wrap-passphrase "$wrappedfnek" - unset key fnek echo | cat - "$wrappedkey" "$wrappedfnek" > "$KEYFILE" if [ ! -f "$CHROOTSRC/.ecryptfs" ]; then echo "$KEYFILE" > "$CHROOTSRC/.ecryptfs" fi elif [ ! -f "$KEYFILE" ]; then error 1 "Unable to find encryption key file $KEYFILE" else echo_tty -n "Enter encryption passphrase for $NAME: " [ -t 0 ] && stty -echo if [ -z "$passphrase" ]; then read -r passphrase fi [ -t 0 ] && stty echo echo_tty '' wrappedkey="`mktemp`" wrappedfnek="`mktemp`" addtrap "rm -f '$wrappedkey' '$wrappedfnek'" # Extract wrapped keys from keyfile wrappedtotal="$(($(wc -c < "$KEYFILE") - $(head -n 1 "$KEYFILE" | wc -c)))" wrappedsize="$((wrappedtotal / 2))" tail -c "$wrappedtotal" "$KEYFILE" | head -c "$wrappedsize" > "$wrappedkey" tail -c "$wrappedsize" "$KEYFILE" > "$wrappedfnek" # Change the passphrase if requested if [ "${ENCRYPT:-0}" -ge 2 ]; then oldpassphrase="$passphrase" passphrase="$CROUTON_NEW_PASSPHRASE" promptNewPassphrase echo "Applying passphrase change" 1>&2 echo -n "$oldpassphrase $passphrase" | ecryptfs-rewrap-passphrase "$wrappedkey" - echo -n "$oldpassphrase $passphrase" | ecryptfs-rewrap-passphrase "$wrappedfnek" - echo | cat - "$wrappedkey" "$wrappedfnek" > "$KEYFILE" unset oldpassphrase fi fi # Add keys to keychain and extract keysig="`echo -n "$passphrase" \ | ecryptfs-unwrap-passphrase "$wrappedkey" - 2>/dev/null \ | ecryptfs-add-passphrase - 2>/dev/null \ | sed -n 's/.*\[\([0-9a-zA-Z]*\)\].*/\1/p'`" fneksig="`echo -n "$passphrase" \ | ecryptfs-unwrap-passphrase "$wrappedfnek" - 2>/dev/null \ | ecryptfs-add-passphrase - 2>/dev/null \ | sed -n 's/.*\[\([0-9a-zA-Z]*\)\].*/\1/p'`" if [ -z "$keysig" -o -z "$fneksig" ]; then error 1 "Failed to decrypt $NAME." fi # Create a new session, and link user keyring to that session, # as required by ecryptfs. keyctl new_session >/dev/null keyctl link @u @s mnt="ecryptfs_sig=$keysig,ecryptfs_fnek_sig=$fneksig" mnt="$mnt,ecryptfs_cipher=aes,ecryptfs_key_bytes=16" mnt="$mnt,ecryptfs_unlink_sigs,$MOUNTOPTS" if ! mount -i -t ecryptfs -o "$mnt" "$CHROOTSRC" "$CHROOT"; then error 1 "Failed to mount $NAME." fi mount -i -o 'remount,symfollow' "$CHROOT" 2>/dev/null || true whitelist "$CHROOT" fi # Perform the move if [ -z "$movesrc" ]; then continue fi response=y for file in "$movesrc/"*; do if [ "${file#*/ECRYPTFS_FNEK_ENCRYPTED}" != "$file" ]; then echo -n \ "About to continue encrypting the unencrypted portion of $NAME. If this is unexpected, then it could mean that someone's trying to inject files into your encrypted chroot, potentially allowing them to steal your data. Please choose one of the following options: yes -- You are sure you want to continue moving the files in. They're yours. del -- You do not like these files and want them deleted permanently. list -- You do not know what these files are and want to list them. no -- You don't want to decide one way or another quite yet. > " 1>&2 # Don't allow a response to be specified in env unless the password # was also specified in env. if [ -n "$CROUTON_PASSPHRASE" -a \ -n "$CROUTON_MOUNT_RESPONSE" ]; then response="$CROUTON_MOUNT_RESPONSE" echo "$response" 1>&2 else read -r response fi break fi done case "$response" in y*|Y*) ( echo -n "Encrypting $NAME; please wait..." 1>&2 cd "$movesrc" tmp="`mktemp -d --tmpdir=. 'ECRYPTFS_MOVE_STAGING_XXXXXX'`" find -not -name 'ECRYPTFS_FNEK_ENCRYPTED*' \ -not -wholename './ECRYPTFS_MOVE_STAGING_*' \ -not -wholename '.' \ -not -wholename './.ecryptfs' \ -not -wholename './.crouton-targets' \ -exec mkdir -p "$tmp/{}" ';' \ -exec rmdir "$tmp/{}" ';' \ '(' -prune , -exec mv -fT '{}' "$tmp/{}" ';' ')' 1>&2 for tmp in ECRYPTFS_MOVE_STAGING_*; do ( cd "$tmp" find '!' '(' -type d -exec test -d "$CHROOT/{}" ';' ')' \ '(' -prune , -exec mv -fT '{}' "$CHROOT/{}" ';' ')' \ -exec echo -n . ';' 1>&2 find -depth -type d -not -wholename . \ -exec test -d "$CHROOT/{}" ';' \ -exec rmdir '{}' ';' 1>&2 ) rmdir "$tmp" 2>/dev/null || true done echo 'done.' 1>&2 );; d*|D*) ( echo "Deleting unencrypted files in $NAME; please wait..." 1>&2 cd "$movesrc" find -not -name 'ECRYPTFS_FNEK_ENCRYPTED*' \ -not -wholename '.' \ -not -wholename './.ecryptfs' \ -not -wholename './.crouton-targets' \ '(' -prune , -exec rm -rvf '{}' ';' ')' 1>&2 echo 'Done.' 1>&2 );; l*|L*) ( echo "Listing unencrypted files in $NAME; please wait..." 1>&2 cd "$movesrc" find -not -name 'ECRYPTFS_FNEK_ENCRYPTED*' \ -not -name 'ECRYPTFS_MOVE_STAGING_*' \ -not -wholename '.' \ -not -wholename './.ecryptfs' \ -not -wholename './.crouton-targets' \ -print -prune | cut -b2- 1>&2 echo 'Done.' 1>&2 exit 2 );; esac done exit 0 ================================================ FILE: host-bin/startcli ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" USAGE="$APPLICATION [options] Wraps enter-chroot to start a CLI session in a new VT. By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." if [ -f /sbin/frecon ]; then # Until we get a stable interface to controlling frecon, just use the pty openvt='' else openvt='openvt -vws --' fi export TERM='linux' exec sh -e "`dirname "\`readlink -f -- "$0"\`"`/enter-chroot" -t cli-extra -l \ "$@" "" $openvt ================================================ FILE: host-bin/starte17 ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" USAGE="$APPLICATION [options] Wraps enter-chroot to start an e17 session. By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." exec sh -e "`dirname "\`readlink -f -- "$0"\`"`/enter-chroot" -t e17 "$@" "" \ exec xinit /usr/bin/enlightenment_start ================================================ FILE: host-bin/startgnome ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" USAGE="$APPLICATION [options] Wraps enter-chroot to start a GNOME session. By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." exec sh -e "`dirname "\`readlink -f -- "$0"\`"`/enter-chroot" -t gnome "$@" "" \ exec startgnome ================================================ FILE: host-bin/startkde ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" USAGE="$APPLICATION [options] Wraps enter-chroot to start a KDE session. By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." exec sh -e "`dirname "\`readlink -f -- "$0"\`"`/enter-chroot" -t kde "$@" "" \ exec xinit /usr/bin/startkde ================================================ FILE: host-bin/startkodi ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" USAGE="$APPLICATION [options] Wraps enter-chroot to start an KODI session. By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." exec sh -e "`dirname "\`readlink -f -- "$0"\`"`/enter-chroot" -t kodi "$@" "" \ exec croutonpowerd -i xinit /usr/bin/kodi --standalone ================================================ FILE: host-bin/startlxde ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" USAGE="$APPLICATION [options] Wraps enter-chroot to start an LXDE session. By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." exec sh -e "`dirname "\`readlink -f -- "$0"\`"`/enter-chroot" -t lxde "$@" "" \ exec xinit /usr/bin/startlxde ================================================ FILE: host-bin/startunity ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" USAGE="$APPLICATION [options] Wraps enter-chroot to start a Unity session. By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." exec sh -e "`dirname "\`readlink -f -- "$0"\`"`/enter-chroot" -t unity "$@" "" \ exec startunity ================================================ FILE: host-bin/startxfce4 ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" USAGE="$APPLICATION [options] Wraps enter-chroot to start an Xfce session. By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." exec sh -e "`dirname "\`readlink -f -- "$0"\`"`/enter-chroot" -t xfce "$@" "" \ exec startxfce4 ================================================ FILE: host-bin/startxiwi ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" ENTERCHROOT="$(dirname "$(readlink -f -- "$0")")/enter-chroot" OPTS_ENTER='' OPTS_XIWI='' USAGE="$APPLICATION [options] chroot_app [parameters] Wraps enter-chroot to launch a window or tab in Chromium OS for any graphical application. Applications launched in this way show in independent windows or tabs. By default, it will use the primary user on the first xiwi-enabled chroot found and launch the chroot_app in a window. Options: $("$ENTERCHROOT" -h 2>&1 | grep -e ' -[bckntu]') -F Launch the chroot_app full-screen. -T Launch the chroot_app in a tab. -f Prevent xiwi from quitting automatically. (see NOTE below) NOTE: xiwi will normally close when the application returns. Some gui applications fork before or during normal operation, which can confuse xiwi and cause it to quit prematurely. If your application does not have a parameter that prevents it from forking, and crouton is unable to automatically detect the fork, you can use -f to prevent xiwi from quitting automatically. xiwi will quit if you close the Chromium OS window when nothing is displayed. You can cycle through multiple windows inside the application via Ctrl-Alt-Tab/Ctrl-Alt-Shift-Tab, or close them via Ctrl-Alt-Shift-Escape. If the chroot_app begins with 'start' but you still want to use the default window manager, specify the full path of the application. " while getopts 'bc:k:n:t:u:FTf' OPT; do case "$OPT" in b) OPTS_ENTER="$OPTS_ENTER -$OPT";; c|k|n|t|u) OPTARG="$(echo -n "$OPTARG" | sed -e "s/'/'\\\\\\''/g")" OPTS_ENTER="$OPTS_ENTER -$OPT '$OPTARG'";; f|F|T) OPTS_XIWI="$OPTS_XIWI -$OPT";; \?) echo "$USAGE" 1>&2 exit 2;; esac done shift "$((OPTIND-1))" if [ "$#" = "0" ]; then echo "$USAGE" 1>&2 exit 2 fi eval "exec sh -e \"\$ENTERCHROOT\" -t xiwi $OPTS_ENTER \ exec xiwi $OPTS_XIWI \"\$@\"" ================================================ FILE: host-bin/unmount-chroot ================================================ #!/bin/sh -e # Copyright (c) 2016 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e APPLICATION="${0##*/}" ALLCHROOTS='' BINDIR="`dirname "\`readlink -f -- "$0"\`"`" CHROOTS="`readlink -m -- "$BINDIR/../chroots"`" EXCLUDEROOT='' FORCE='' PRINT='' SIGNAL='TERM' TRIES=5 YES='' ROOT="`readlink -m -- '/var/run/crouton'`" USAGE="$APPLICATION [options] name [...] Unmounts one or more chroots, optionally killing any processes still running inside them. By default, it will run in interactive mode where it will ask to kill any remaining processes if unable to unmount the chroot within 5 seconds. Options: -a Unmount all chroots in the CHROOTS directory. -c CHROOTS Directory the chroots are in. Default: $CHROOTS -f Forces a chroot to unmount, potentially breaking or killing other instances of the same chroot. -k KILL Send the processes SIGKILL instead of SIGTERM. -p Print to STDOUT the processes stopping a chroot from unmounting. -t TRIES Number of seconds to try before signalling the processes. Use -t inf to be exceedingly patient. Default: $TRIES -x Keep the root directory of the chroot mounted. -y Signal any remaining processes without confirmation. Automatically escalates from SIGTERM to SIGKILL." # Common functions . "$BINDIR/../installer/functions" # Process arguments getopts_string='ac:fkpt:xy' while getopts_nextarg; do case "$getopts_var" in a) ALLCHROOTS='y';; c) CHROOTS="`readlink -m -- "$getopts_arg"`";; f) FORCE='y';; k) SIGNAL="KILL";; p) PRINT='y';; t) TRIES="$getopts_arg";; x) EXCLUDEROOT='y';; y) YES='a';; \?) error 2 "$USAGE";; esac done # Need at least one chroot listed, or -a; not both. if [ $# = 0 -a -z "$ALLCHROOTS" ] || [ $# != 0 -a -n "$ALLCHROOTS" ]; then error 2 "$USAGE" fi # Make sure TRIES is valid if [ "$TRIES" = inf ]; then TRIES=-1 elif [ "$TRIES" -lt -1 ]; then error 2 "$USAGE" fi # We need to run as root if [ "$USER" != root -a "$UID" != 0 ]; then error 2 "$APPLICATION must be run as root." fi # Check if a chroot is running with this directory. We detect the # appropriate commands by checking if the command's parent root is not equal # to the pid's root. This avoids not unmounting due to a lazy-quitting # background application within the chroot. We also don't consider processes # that have a parent PID of 1 or that of session_manager's (which would mean an # orphaned process in this case), as enter-chroot never orphans its children. # $1: $base; the canonicalized base path of the chroot # Returns: non-zero if the chroot is in use. checkusage() { if [ -n "$FORCE" ]; then return 0 fi local b="${1%/}/" pid ppid proot prootdir root rootdir pids='' local smgrpid="`pgrep -o -u 0 -x session_manager || echo 1`" for root in /proc/*/root; do if [ ! -r "$root" ]; then continue fi rootdir="`readlink -f -- "$root"`" rootdir="${rootdir%/}/" if [ "${rootdir#"$b"}" = "$rootdir" ]; then continue fi pid="${root#/proc/}" pid="${pid%/root}" ppid="`ps -p "$pid" -o ppid= 2>/dev/null | sed 's/ //g'`" if [ -z "$ppid" ] || [ "$ppid" -eq 1 -o "$ppid" -eq "$smgrpid" ]; then continue fi proot="/proc/$ppid/root" if [ -r "$proot" ]; then prootdir="`readlink -f -- "$proot"`" if [ "${prootdir%/}/" = "$rootdir" ]; then continue fi fi if [ -n "$PRINT" ]; then pids="$pids $pid" continue fi return 1 done if [ -n "$PRINT" -a -n "$pids" ]; then ps -p "${pids# }" -o pid= -o cmd= || true return 1 fi return 0 } # If we specified all chroots, bring in all chroots. if [ -n "$ALLCHROOTS" ]; then if [ ! -d "$CHROOTS" ]; then error 1 "$CHROOTS not found." fi set -- "$CHROOTS/"* fi # Follows and fixes dangerous symlinks, returning the canonicalized path. fixabslinks() { local p="$CHROOT/$1" c # Follow and fix dangerous absolute symlinks while c="`readlink -m -- "$p"`" && [ "$c" != "$p" ]; do p="$CHROOT${c#"$CHROOT"}" done echo "$p" } # Unmount the specified chroot $1 # sets oldstyle if the chroot was unmounted in an old location. # if oldstyle is set upon entry, skips the check for old-style mounts. unmount() { NAME="${1#"$CHROOTS/"}" # Check for existence CHROOT="$CHROOTS/$NAME" if [ ! -d "$CHROOT" ]; then if [ -z "$ALLCHROOTS" ]; then echo "$CHROOT not found." 1>&2 ret=1 fi return 0 fi # Switch to the true mount point, but sort of support old-style mounted # chroots with minimal false-positives to ease transition. Don't unmount # old-style twice in a row, though. CHROOTSRC="$CHROOT" oldencr="$CHROOTS/.secure/$NAME" if mountpoint -q "$oldencr" \ && [ -d "$oldencr/etc/crouton" -a -z "$oldstyle" ]; then # Old encrypted chroots oldstyle='y' CHROOT="$oldencr" echo "$CHROOTSRC appears to be mounted in $CHROOT" 1>&2 elif mountpoint -q "$CHROOT" \ && [ -d "$CHROOT/etc/crouton" -a -z "$oldstyle" ]; then # Keep the chroot the same oldstyle='y' echo "$CHROOTSRC appears to be mounted in place" 1>&2 else oldstyle='' CHROOT="$ROOT/${CHROOT#/}" if [ ! -d "$CHROOT" ]; then # Not mounted return 0 fi fi base="`readlink -f -- "$CHROOT"`" if ! checkusage "$base"; then echo "Not unmounting $CHROOTSRC as another instance is using it." 1>&2 ret=1 return 0 fi # Kill the chroot's system dbus if one is running; failure is fine env -i chroot "$CHROOT" su -s '/bin/sh' -c ' pidfile="/var/run/dbus/pid" if [ ! -f "$pidfile" ]; then exit 0 fi pid="`cat "$pidfile"`" if ! grep -q "^dbus-daemon" "/proc/$pid/cmdline" 2>/dev/null; then exit 0 fi kill $pid' - root 2>/dev/null || true # Unmount all mounts ntries=0 if [ -z "$EXCLUDEROOT" ]; then echo "Unmounting $CHROOTSRC..." 1>&2 else echo "Pruning $CHROOTSRC mounts..." 1>&2 fi baseesc="`echo "$base" | sed 's= =//=g'`" # Define the mountpoint filter to only unmount specific mounts. # The filter is run on the escaped version of the mountpoint. filter() { if [ -z "$EXCLUDEROOT" ]; then grep "^$baseesc\\(/.*\\)\\?\$" else # Don't include the base directory grep "^$baseesc/." fi } # Sync for safety sync # Make sure the chroot's system media bind-mount is marked as slave to avoid # unmounting devices system-wide. We still want to unmount locally-mounted # media, though. media="`fixabslinks '/var/host/media'`" if mountpoint -q "$media"; then mount --make-rslave "$media" fi # Some /proc/mounts entries may end with \040(deleted), in that case, try to # umount them with and without the suffix (in the unlikely case the mount # point actually ends with ' (deleted)') # umount has a bug and may return 0 when many mount points cannot be # unmounted, so we call it once per mount point ('-n 1') while ! sed "s=\\\\040=//=g" /proc/mounts | cut -d' ' -f2 | filter \ | sed -e 's=//= =g;s/^\(\(.*\) (deleted)\)$/\1\n\2/' \ | sort -r | xargs --no-run-if-empty -d ' ' -n 1 umount 2>/dev/null; do if [ "$ntries" -eq "$TRIES" ]; then # Send signal to all processes running under the chroot # ...but confirm first. printonly='' if [ "${YES#[Aa]}" = "$YES" ]; then echo -n "Failed to unmount $CHROOTSRC. Kill processes? [a/k/y/p/N] " 1>&2 if [ -n "$CROUTON_UNMOUNT_RESPONSE" ]; then YES="$CROUTON_UNMOUNT_RESPONSE" echo "$YES" 1>&2 else read -r YES fi if [ "${YES#[Kk]}" != "$YES" ]; then SIGNAL='KILL' elif [ "${YES#[Pp]}" != "$YES" ]; then printonly=y elif [ "${YES#[AaYy]}" = "$YES" ]; then echo "Skipping unmounting of $CHROOTSRC" 1>&2 ret=1 break fi fi if [ -z "$printonly" ]; then echo "Sending SIG$SIGNAL to processes under $CHROOTSRC..." 1>&2 fi for root in /proc/*/root; do if [ ! -r "$root" ] \ || [ ! "`readlink -f -- "$root"`" = "$base" ]; then continue fi pid="${root#/proc/}" pid="${pid%/root}" if [ -n "${printonly:-"$PRINT"}" ]; then ps -p "$pid" -o pid= -o cmd= || true fi if [ -z "$printonly" ]; then kill "-$SIGNAL" "$pid" 2>/dev/null || true fi done # Escalate if [ "${YES#[Aa]}" != "$YES" ]; then SIGNAL='KILL' fi if [ -z "$printonly" ]; then ntries=0 fi else ntries="$((ntries+1))" fi sleep 1 if ! checkusage "$base"; then echo "Aborting unmounting $CHROOTSRC as another instance has begun using it." 1>&2 ret=1 break fi done # More sync for more safety sync } # Unmount each chroot ret=0 for NAME in "$@"; do if [ -z "$NAME" ]; then continue fi oldstyle='' unmount "$NAME" # If we unmounted old-style, do it again in case the new-style was also mounted. if [ -n "$oldstyle" ]; then unmount "$NAME" fi done # HACK: restart debugd when running tests to avoid namespace issues. # This will go away when we start using mount namespaces. if [ -n "$CROUTON_UNMOUNT_RESTART_DEBUGD" ]; then restart debugd >/dev/null || true fi # Re-disable USB persistence (the Chromium OS default) if we no longer # have chroots running with a root in removable media if checkusage "$ROOT/media"; then for usbp in /sys/bus/usb/devices/*/power/persist; do if [ -e "$usbp" ]; then echo 0 > "$usbp" fi done fi exit $ret ================================================ FILE: host-ext/.gitignore ================================================ crouton.crx crouton.zip crouton.pem crouton/kiwi.pexe ================================================ FILE: host-ext/crouton/background.html ================================================