├── .clang-format ├── .envrc ├── .github ├── FUNDING.yaml └── workflows │ └── build.yaml ├── .gitignore ├── CMakeLists.txt ├── flake.lock ├── flake.nix ├── license ├── main.c ├── module.nix ├── noshell.h.in ├── package.nix ├── readme.md └── shell.nix /.clang-format: -------------------------------------------------------------------------------- 1 | PointerAlignment: Left 2 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | use nix 3 | 4 | export CMAKE_EXPORT_COMPILE_COMMANDS=YES 5 | export CMAKE_BUILD_TYPE=Debug 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yaml: -------------------------------------------------------------------------------- 1 | github: [viperML] 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build project 2 | 3 | on: 4 | pull_request: 5 | push: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | main: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Install Nix 14 | uses: DeterminateSystems/nix-installer-action@main 15 | 16 | - name: Build 17 | run: nix build "github:$GITHUB_REPOSITORY/$GITHUB_SHA" -L 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.direnv 2 | *result* 3 | /build 4 | compile_commands.json 5 | /.cache 6 | 7 | noshell.h 8 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.28) 2 | set(CMAKE_C_STANDARD 23) 3 | 4 | project(noshell VERSION 0.1.0 LANGUAGES C) 5 | 6 | add_compile_options( 7 | -Wall 8 | -Wextra 9 | -pedantic 10 | -Wshadow 11 | ) 12 | 13 | 14 | set(DEFAULT_SHELL "/bin/sh" CACHE STRING "Default shell to use as a fallback") 15 | 16 | configure_file(noshell.h.in ${CMAKE_CURRENT_SOURCE_DIR}/noshell.h @ONLY) 17 | 18 | add_executable(${PROJECT_NAME} main.c) 19 | 20 | install(TARGETS ${PROJECT_NAME}) 21 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1714253743, 6 | "narHash": "sha256-mdTQw2XlariysyScCv2tTE45QSU9v/ezLcHJ22f0Nxc=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "58a1abdbae3217ca6b702f03d3b35125d88a2994", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "nixos-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 4 | }; 5 | 6 | outputs = { 7 | self, 8 | nixpkgs, 9 | }: let 10 | forAllSystems = function: 11 | nixpkgs.lib.genAttrs [ 12 | "x86_64-linux" 13 | "aarch64-linux" 14 | ] (system: function nixpkgs.legacyPackages.${system}); 15 | in { 16 | packages = forAllSystems (pkgs: { 17 | default = pkgs.callPackage ./package.nix {}; 18 | }); 19 | 20 | devShells = forAllSystems (pkgs: { 21 | default = import ./shell.nix {inherit pkgs;}; 22 | }); 23 | 24 | nixosModules.default = ./module.nix; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | EUROPEAN UNION PUBLIC LICENCE v. 1.2 2 | EUPL © the European Union 2007, 2016 3 | 4 | This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined 5 | below) which is provided under the terms of this Licence. Any use of the Work, 6 | other than as authorised under this Licence is prohibited (to the extent such 7 | use is covered by a right of the copyright holder of the Work). 8 | 9 | The Work is provided under the terms of this Licence when the Licensor (as 10 | defined below) has placed the following notice immediately following the 11 | copyright notice for the Work: 12 | 13 | Licensed under the EUPL 14 | 15 | or has expressed by any other means his willingness to license under the EUPL. 16 | 17 | 1. Definitions 18 | 19 | In this Licence, the following terms have the following meaning: 20 | 21 | - ‘The Licence’: this Licence. 22 | 23 | - ‘The Original Work’: the work or software distributed or communicated by the 24 | Licensor under this Licence, available as Source Code and also as Executable 25 | Code as the case may be. 26 | 27 | - ‘Derivative Works’: the works or software that could be created by the 28 | Licensee, based upon the Original Work or modifications thereof. This Licence 29 | does not define the extent of modification or dependence on the Original Work 30 | required in order to classify a work as a Derivative Work; this extent is 31 | determined by copyright law applicable in the country mentioned in Article 15. 32 | 33 | - ‘The Work’: the Original Work or its Derivative Works. 34 | 35 | - ‘The Source Code’: the human-readable form of the Work which is the most 36 | convenient for people to study and modify. 37 | 38 | - ‘The Executable Code’: any code which has generally been compiled and which is 39 | meant to be interpreted by a computer as a program. 40 | 41 | - ‘The Licensor’: the natural or legal person that distributes or communicates 42 | the Work under the Licence. 43 | 44 | - ‘Contributor(s)’: any natural or legal person who modifies the Work under the 45 | Licence, or otherwise contributes to the creation of a Derivative Work. 46 | 47 | - ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of 48 | the Work under the terms of the Licence. 49 | 50 | - ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, 51 | renting, distributing, communicating, transmitting, or otherwise making 52 | available, online or offline, copies of the Work or providing access to its 53 | essential functionalities at the disposal of any other natural or legal 54 | person. 55 | 56 | 2. Scope of the rights granted by the Licence 57 | 58 | The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, 59 | sublicensable licence to do the following, for the duration of copyright vested 60 | in the Original Work: 61 | 62 | - use the Work in any circumstance and for all usage, 63 | - reproduce the Work, 64 | - modify the Work, and make Derivative Works based upon the Work, 65 | - communicate to the public, including the right to make available or display 66 | the Work or copies thereof to the public and perform publicly, as the case may 67 | be, the Work, 68 | - distribute the Work or copies thereof, 69 | - lend and rent the Work or copies thereof, 70 | - sublicense rights in the Work or copies thereof. 71 | 72 | Those rights can be exercised on any media, supports and formats, whether now 73 | known or later invented, as far as the applicable law permits so. 74 | 75 | In the countries where moral rights apply, the Licensor waives his right to 76 | exercise his moral right to the extent allowed by law in order to make effective 77 | the licence of the economic rights here above listed. 78 | 79 | The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to 80 | any patents held by the Licensor, to the extent necessary to make use of the 81 | rights granted on the Work under this Licence. 82 | 83 | 3. Communication of the Source Code 84 | 85 | The Licensor may provide the Work either in its Source Code form, or as 86 | Executable Code. If the Work is provided as Executable Code, the Licensor 87 | provides in addition a machine-readable copy of the Source Code of the Work 88 | along with each copy of the Work that the Licensor distributes or indicates, in 89 | a notice following the copyright notice attached to the Work, a repository where 90 | the Source Code is easily and freely accessible for as long as the Licensor 91 | continues to distribute or communicate the Work. 92 | 93 | 4. Limitations on copyright 94 | 95 | Nothing in this Licence is intended to deprive the Licensee of the benefits from 96 | any exception or limitation to the exclusive rights of the rights owners in the 97 | Work, of the exhaustion of those rights or of other applicable limitations 98 | thereto. 99 | 100 | 5. Obligations of the Licensee 101 | 102 | The grant of the rights mentioned above is subject to some restrictions and 103 | obligations imposed on the Licensee. Those obligations are the following: 104 | 105 | Attribution right: The Licensee shall keep intact all copyright, patent or 106 | trademarks notices and all notices that refer to the Licence and to the 107 | disclaimer of warranties. The Licensee must include a copy of such notices and a 108 | copy of the Licence with every copy of the Work he/she distributes or 109 | communicates. The Licensee must cause any Derivative Work to carry prominent 110 | notices stating that the Work has been modified and the date of modification. 111 | 112 | Copyleft clause: If the Licensee distributes or communicates copies of the 113 | Original Works or Derivative Works, this Distribution or Communication will be 114 | done under the terms of this Licence or of a later version of this Licence 115 | unless the Original Work is expressly distributed only under this version of the 116 | Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee 117 | (becoming Licensor) cannot offer or impose any additional terms or conditions on 118 | the Work or Derivative Work that alter or restrict the terms of the Licence. 119 | 120 | Compatibility clause: If the Licensee Distributes or Communicates Derivative 121 | Works or copies thereof based upon both the Work and another work licensed under 122 | a Compatible Licence, this Distribution or Communication can be done under the 123 | terms of this Compatible Licence. For the sake of this clause, ‘Compatible 124 | Licence’ refers to the licences listed in the appendix attached to this Licence. 125 | Should the Licensee's obligations under the Compatible Licence conflict with 126 | his/her obligations under this Licence, the obligations of the Compatible 127 | Licence shall prevail. 128 | 129 | Provision of Source Code: When distributing or communicating copies of the Work, 130 | the Licensee will provide a machine-readable copy of the Source Code or indicate 131 | a repository where this Source will be easily and freely available for as long 132 | as the Licensee continues to distribute or communicate the Work. 133 | 134 | Legal Protection: This Licence does not grant permission to use the trade names, 135 | trademarks, service marks, or names of the Licensor, except as required for 136 | reasonable and customary use in describing the origin of the Work and 137 | reproducing the content of the copyright notice. 138 | 139 | 6. Chain of Authorship 140 | 141 | The original Licensor warrants that the copyright in the Original Work granted 142 | hereunder is owned by him/her or licensed to him/her and that he/she has the 143 | power and authority to grant the Licence. 144 | 145 | Each Contributor warrants that the copyright in the modifications he/she brings 146 | to the Work are owned by him/her or licensed to him/her and that he/she has the 147 | power and authority to grant the Licence. 148 | 149 | Each time You accept the Licence, the original Licensor and subsequent 150 | Contributors grant You a licence to their contributions to the Work, under the 151 | terms of this Licence. 152 | 153 | 7. Disclaimer of Warranty 154 | 155 | The Work is a work in progress, which is continuously improved by numerous 156 | Contributors. It is not a finished work and may therefore contain defects or 157 | ‘bugs’ inherent to this type of development. 158 | 159 | For the above reason, the Work is provided under the Licence on an ‘as is’ basis 160 | and without warranties of any kind concerning the Work, including without 161 | limitation merchantability, fitness for a particular purpose, absence of defects 162 | or errors, accuracy, non-infringement of intellectual property rights other than 163 | copyright as stated in Article 6 of this Licence. 164 | 165 | This disclaimer of warranty is an essential part of the Licence and a condition 166 | for the grant of any rights to the Work. 167 | 168 | 8. Disclaimer of Liability 169 | 170 | Except in the cases of wilful misconduct or damages directly caused to natural 171 | persons, the Licensor will in no event be liable for any direct or indirect, 172 | material or moral, damages of any kind, arising out of the Licence or of the use 173 | of the Work, including without limitation, damages for loss of goodwill, work 174 | stoppage, computer failure or malfunction, loss of data or any commercial 175 | damage, even if the Licensor has been advised of the possibility of such damage. 176 | However, the Licensor will be liable under statutory product liability laws as 177 | far such laws apply to the Work. 178 | 179 | 9. Additional agreements 180 | 181 | While distributing the Work, You may choose to conclude an additional agreement, 182 | defining obligations or services consistent with this Licence. However, if 183 | accepting obligations, You may act only on your own behalf and on your sole 184 | responsibility, not on behalf of the original Licensor or any other Contributor, 185 | and only if You agree to indemnify, defend, and hold each Contributor harmless 186 | for any liability incurred by, or claims asserted against such Contributor by 187 | the fact You have accepted any warranty or additional liability. 188 | 189 | 10. Acceptance of the Licence 190 | 191 | The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ 192 | placed under the bottom of a window displaying the text of this Licence or by 193 | affirming consent in any other similar way, in accordance with the rules of 194 | applicable law. Clicking on that icon indicates your clear and irrevocable 195 | acceptance of this Licence and all of its terms and conditions. 196 | 197 | Similarly, you irrevocably accept this Licence and all of its terms and 198 | conditions by exercising any rights granted to You by Article 2 of this Licence, 199 | such as the use of the Work, the creation by You of a Derivative Work or the 200 | Distribution or Communication by You of the Work or copies thereof. 201 | 202 | 11. Information to the public 203 | 204 | In case of any Distribution or Communication of the Work by means of electronic 205 | communication by You (for example, by offering to download the Work from a 206 | remote location) the distribution channel or media (for example, a website) must 207 | at least provide to the public the information requested by the applicable law 208 | regarding the Licensor, the Licence and the way it may be accessible, concluded, 209 | stored and reproduced by the Licensee. 210 | 211 | 12. Termination of the Licence 212 | 213 | The Licence and the rights granted hereunder will terminate automatically upon 214 | any breach by the Licensee of the terms of the Licence. 215 | 216 | Such a termination will not terminate the licences of any person who has 217 | received the Work from the Licensee under the Licence, provided such persons 218 | remain in full compliance with the Licence. 219 | 220 | 13. Miscellaneous 221 | 222 | Without prejudice of Article 9 above, the Licence represents the complete 223 | agreement between the Parties as to the Work. 224 | 225 | If any provision of the Licence is invalid or unenforceable under applicable 226 | law, this will not affect the validity or enforceability of the Licence as a 227 | whole. Such provision will be construed or reformed so as necessary to make it 228 | valid and enforceable. 229 | 230 | The European Commission may publish other linguistic versions or new versions of 231 | this Licence or updated versions of the Appendix, so far this is required and 232 | reasonable, without reducing the scope of the rights granted by the Licence. New 233 | versions of the Licence will be published with a unique version number. 234 | 235 | All linguistic versions of this Licence, approved by the European Commission, 236 | have identical value. Parties can take advantage of the linguistic version of 237 | their choice. 238 | 239 | 14. Jurisdiction 240 | 241 | Without prejudice to specific agreement between parties, 242 | 243 | - any litigation resulting from the interpretation of this License, arising 244 | between the European Union institutions, bodies, offices or agencies, as a 245 | Licensor, and any Licensee, will be subject to the jurisdiction of the Court 246 | of Justice of the European Union, as laid down in article 272 of the Treaty on 247 | the Functioning of the European Union, 248 | 249 | - any litigation arising between other parties and resulting from the 250 | interpretation of this License, will be subject to the exclusive jurisdiction 251 | of the competent court where the Licensor resides or conducts its primary 252 | business. 253 | 254 | 15. Applicable Law 255 | 256 | Without prejudice to specific agreement between parties, 257 | 258 | - this Licence shall be governed by the law of the European Union Member State 259 | where the Licensor has his seat, resides or has his registered office, 260 | 261 | - this licence shall be governed by Belgian law if the Licensor has no seat, 262 | residence or registered office inside a European Union Member State. 263 | 264 | Appendix 265 | 266 | ‘Compatible Licences’ according to Article 5 EUPL are: 267 | 268 | - GNU General Public License (GPL) v. 2, v. 3 269 | - GNU Affero General Public License (AGPL) v. 3 270 | - Open Software License (OSL) v. 2.1, v. 3.0 271 | - Eclipse Public License (EPL) v. 1.0 272 | - CeCILL v. 2.0, v. 2.1 273 | - Mozilla Public Licence (MPL) v. 2 274 | - GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 275 | - Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for 276 | works other than software 277 | - European Union Public Licence (EUPL) v. 1.1, v. 1.2 278 | - Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong 279 | Reciprocity (LiLiQ-R+). 280 | 281 | The European Commission may update this Appendix to later versions of the above 282 | licences without producing a new version of the EUPL, as long as they provide 283 | the rights granted in Article 2 of this Licence and protect the covered Source 284 | Code from exclusive appropriation. 285 | 286 | All other changes or additions to this Appendix require the production of a new 287 | EUPL version. 288 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "noshell.h" 12 | 13 | static bool DEBUG = false; 14 | 15 | #define eprint_d(...) \ 16 | { \ 17 | if (DEBUG) { \ 18 | fprintf(stderr, __VA_ARGS__); \ 19 | } \ 20 | }; 21 | 22 | #define eprint(...) \ 23 | { fprintf(stderr, __VA_ARGS__); }; 24 | 25 | bool usable(char const* path) { 26 | char real[PATH_MAX]; 27 | if (realpath(path, real) == NULL) { 28 | return false; 29 | } 30 | 31 | if (access(real, X_OK) == 0) { 32 | struct stat sb = {}; 33 | if (stat(real, &sb) == 0 && S_ISREG(sb.st_mode)) { 34 | return true; 35 | } else { 36 | return false; 37 | } 38 | } else { 39 | char* msg; 40 | int errsv = errno; 41 | 42 | switch (errsv) { 43 | case EACCES: 44 | break; 45 | default: 46 | asprintf(&msg, "WARN: Checking %s", path); 47 | perror(msg); 48 | free(msg); 49 | } 50 | 51 | return false; 52 | } 53 | } 54 | 55 | char* getshell(void) { 56 | char* relpath = "shell"; 57 | char* xdg_config_home = getenv("XDG_CONFIG_HOME"); 58 | 59 | char* xdg_result = NULL; 60 | if (xdg_config_home != NULL) { 61 | asprintf(&xdg_result, "%s/%s", xdg_config_home, relpath); 62 | if (usable(xdg_result)) { 63 | return xdg_result; 64 | } 65 | } 66 | 67 | char* home = getenv("HOME"); 68 | if (home != NULL) { 69 | char* home_result; 70 | asprintf(&home_result, "%s/.config/%s", home, relpath); 71 | if (xdg_config_home == NULL) { 72 | if (usable(home_result)) { 73 | return home_result; 74 | } 75 | } else { 76 | if (strcmp(home_result, xdg_result) && usable(home_result)) { 77 | return home_result; 78 | }; 79 | } 80 | } 81 | 82 | // eprint("WARN: Using fallback shell\n"); 83 | eprint_d("INFO: Using fallback shell\n"); 84 | 85 | return strdup(DEFAULT_SHELL); 86 | } 87 | 88 | /// Changes argv0 depending on the filetype 89 | /// - Regular file: uses the basename 90 | /// - Symlink: dereferences the symlink once and uses the basename 91 | void argv0_deref(char** argv0_p) { 92 | struct stat sb = {}; 93 | if (lstat(*argv0_p, &sb) != 0) { 94 | eprint("WARN: Failed to stat the shell\n"); 95 | return; 96 | } 97 | 98 | if (S_ISREG(sb.st_mode)) { 99 | *argv0_p = strdup(basename(*argv0_p)); 100 | return; 101 | } 102 | 103 | if (S_ISLNK(sb.st_mode)) { 104 | char buf[PATH_MAX + 1]; 105 | if (readlink(*argv0_p, buf, PATH_MAX) == -1) { 106 | eprint("Failed to readlink\n"); 107 | return; 108 | } 109 | *argv0_p = strdup(basename(buf)); 110 | return; 111 | } 112 | } 113 | 114 | int main(int argc, char* argv[]) { 115 | (void)argc; 116 | 117 | if (getenv("NOSHELL_DEBUG") != NULL) { 118 | DEBUG = true; 119 | } 120 | 121 | char* shell = getshell(); 122 | 123 | if (shell == NULL) { 124 | eprint("ERROR: Failed to detect shell"); 125 | return EXIT_FAILURE; 126 | } 127 | 128 | eprint_d("INFO: Using %s\n", shell); 129 | 130 | bool login_dash = argv[0][0] == '-'; 131 | 132 | argv[0] = shell; 133 | argv0_deref(&argv[0]); 134 | 135 | if (login_dash) { 136 | asprintf(&argv[0], "-%s", argv[0]); 137 | } 138 | 139 | execvp(shell, argv); 140 | free(shell); 141 | 142 | perror("ERROR: Failed to execute shell"); 143 | return EXIT_FAILURE; 144 | } 145 | -------------------------------------------------------------------------------- /module.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: let 7 | pkg = pkgs.callPackage ./package.nix {}; 8 | cfg = config.programs.noshell; 9 | in { 10 | options = { 11 | programs.noshell = { 12 | enable = lib.mkEnableOption "noshell, user-configurable login shell"; 13 | package = lib.mkOption { 14 | description = "noshell package"; 15 | default = pkg; 16 | type = lib.types.package; 17 | }; 18 | }; 19 | }; 20 | 21 | config = lib.mkIf cfg.enable { 22 | environment = { 23 | shells = [cfg.package]; 24 | systemPackages = [cfg.package]; 25 | }; 26 | users.defaultUserShell = cfg.package; 27 | 28 | # Default root to bash 29 | # defaultUserShell sets prio to 1000, we want a lower prio but still 30 | # let users change this without mkForce 31 | users.users.root.shell = lib.mkOverride 999 (pkgs.bashInteractive); 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /noshell.h.in: -------------------------------------------------------------------------------- 1 | #ifndef NOSHELL_H 2 | #define NOSHELL_H 3 | 4 | #define DEFAULT_SHELL "@DEFAULT_SHELL@" 5 | 6 | #endif // NOSHELL_H 7 | -------------------------------------------------------------------------------- /package.nix: -------------------------------------------------------------------------------- 1 | { 2 | # Fallback shell to use 3 | # Can be either a shell with a proper passthru.shellPath, or a path to a file 4 | bashInteractive, 5 | defaultShell ? bashInteractive, 6 | # 7 | stdenv, 8 | cmake, 9 | lib, 10 | }: let 11 | defaultShell' = 12 | if lib.isDerivation defaultShell 13 | then "${defaultShell}${defaultShell.passthru.shellPath}" 14 | else defaultShell; 15 | fs = lib.fileset; 16 | r = ./.; 17 | in 18 | stdenv.mkDerivation { 19 | name = "noshell"; 20 | 21 | src = fs.toSource { 22 | root = r; 23 | fileset = fs.intersection (lib.fileset.fromSource (lib.sources.cleanSource r)) ( 24 | lib.fileset.unions [ 25 | (fs.fileFilter (file: file.hasExt "c") r) 26 | (fs.fileFilter (file: file.hasExt "txt") r) 27 | (fs.fileFilter (file: file.hasExt "in") r) 28 | ] 29 | ); 30 | }; 31 | 32 | nativeBuildInputs = [cmake]; 33 | 34 | cmakeFlags = [ 35 | "-DDEFAULT_SHELL=${defaultShell'}" 36 | ]; 37 | 38 | passthru = { 39 | shellPath = "/bin/noshell"; 40 | }; 41 | 42 | meta = { 43 | mainProgram = "noshell"; 44 | description = "User-configurable login shell"; 45 | homepage = "https://github.com/viperML/noshell"; 46 | license = lib.licenses.eupl12; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

noshell

2 | 3 |
user-configurable login shell
4 | 5 | ---- 6 | 7 | Noshell is a simple shim that executes `$XDG_CONFIG_HOME/shell` as your login shell. 8 | If you configure noshell system-wide, users can configure their login shell with 9 | a simple `ln -sT /path/to/zsh ~/.config/shell`, instead of requiring 10 | superuser permissions to modify `/etc/shells`. 11 | 12 | By default, noshell checks the following locations in the order: 13 | 14 | - `$XDG_CONFIG_HOME/shell` 15 | - `$HOME/.config/shell` 16 | - `pkgs.bashInteractive` as the fallback. Can be configured at build-time 17 | 18 | ## Usage 19 | 20 | Add this flake's `nixosModules.default` to your NixOS imports, and set: 21 | ``` 22 | programs.noshell.enable = true 23 | ``` 24 | 25 | Noshell will be configured as the default shell to **ALL** users. You might want to 26 | disable noshell for some user as a fallback, like `users.users.root.shell = pkgs.bashInteractive;`. 27 | 28 | ## Why? 29 | 30 | Noshell enables some patterns in the NixOS ecosystem, such as: 31 | 32 | - Using a shell with a configuration wrapper. As the wrapper is a different program 33 | that is not present in `/etc/shells`. See [https://github.com/viperML/wrapper-manager](https://github.com/viperML/wrapper-manager). 34 | - User-level configuration of the login shell with home-manager (`xdg.configFile."shell".source = lib.getExe pkgs.nushell;`) 35 | 36 | It can be useful too outside of NixOS, for example to use a custom build of some shell. 37 | 38 | ## A note on login shells 39 | 40 | As far as I know, there is nothing "written in stone" about what a login shell must do, but other 41 | tools in Linux do expect some functionality. 42 | 43 | Some of these assumptions are: 44 | - The login shell is expected to `. /etc/profile` by its own. This file is a POSIX sh script that sets up environment variables needed by the session. `pam_env` also 45 | sets environment variables but that is handled earlier in the login process. 46 | - `login` may call your shell with `-progname`, that is with a dash as the first character, to indicate that this is a login shell. 47 | - Other programs may call your shell with the argument `-l` to indicate that it is a login shell. 48 | - Some programs might assume `$SHELL` is something that you can pass arguments to, whatever the arguments are, with `-c`. 49 | 50 | Please report any functionality expected from a login shell in the issues board: [https://github.com/viperML/noshell/issues](https://github.com/viperML/noshell/issues). 51 | 52 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | {pkgs ? import {}}: 2 | with pkgs; 3 | mkShell { 4 | packages = [ 5 | cmake 6 | clang-tools 7 | gdb 8 | ]; 9 | hardeningDisable = ["all"]; 10 | } 11 | --------------------------------------------------------------------------------