├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── binding.gyp ├── lib └── posix │ └── index.js ├── package.json ├── src └── posix.cc ├── test └── unit │ ├── test-chroot.js │ ├── test-getegid.js │ ├── test-geteuid.js │ ├── test-getgrnam.js │ ├── test-gethostname.js │ ├── test-getppid.js │ ├── test-getpwnam.js │ ├── test-getrlimit.js │ ├── test-initgroups.js │ ├── test-setegid.js │ ├── test-seteuid.js │ ├── test-setpgid.js │ ├── test-setregid.js │ ├── test-setreuid.js │ ├── test-setrlimit.js │ ├── test-setsid.js │ └── test-syslog.js └── wscript /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *~ 3 | .lock-wscript 4 | *.log 5 | node_modules 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | .git* 3 | .lock-wscript 4 | build/ 5 | *~ 6 | c4che 7 | *#* 8 | .DS_Store 9 | .npmignore 10 | *.log 11 | *.o 12 | node_modules/ 13 | lib/posix/*~ 14 | test/unit/*~ 15 | .*~ 16 | .* 17 | *.gz 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | language: node_js 4 | node_js: 5 | - "0.10" 6 | - "0.12" 7 | - "4" 8 | - "6" 9 | - "8" 10 | - "10" 11 | - "node" 12 | sudo: true 13 | addons: 14 | apt: 15 | sources: 16 | - ubuntu-toolchain-r-test 17 | packages: 18 | - g++-4.8 19 | install: 20 | - export CXX=g++-4.8 21 | - $CXX --version 22 | - npm install 23 | - make travis 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2015 Mika Eloranta 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | node-command := xargs -n 1 -I file $(shell which node) file $(params) 3 | 4 | default: 5 | @echo need a target name... 6 | 7 | test: test-unit 8 | 9 | bench: 10 | @find benchmark -name "*-bench.js" | $(node-command) 11 | 12 | test-unit: 13 | @find test/unit -name "test-*.js" | $(node-command) 14 | 15 | test-unit-sudo: 16 | @find test/unit -name "test-*.js" | sudo $(node-command) 17 | 18 | travis: test-unit test-unit-sudo 19 | 20 | .PHONY : test 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-posix 2 | 3 | The missing POSIX system calls for Node. 4 | 5 | [![Build Status](https://secure.travis-ci.org/ohmu/node-posix.png)](http://travis-ci.org/ohmu/node-posix) 6 | 7 | [![NPM](https://nodei.co/npm/posix.png?downloads=true&downloadRank=true)](https://nodei.co/npm/posix/) [![NPM](https://nodei.co/npm-dl/posix.png?months=6&height=3)](https://nodei.co/npm/posix/) 8 | 9 | 10 | ## FAQ 11 | 12 | * Q: Why? 13 | * A: Because the Node core has a limited set of POSIX system calls. 14 | * Q: How mature/stable is this? 15 | * A: Each version released in NPM has decent automated test coverage. The 16 | module has been successfully used in busy production sites for several 17 | years. 18 | * Q: I have a feature request/bug report... 19 | * A: Please submit a pull request or an issue ticket at 20 | https://github.com/ohmu/node-posix 21 | 22 | ## Compatibility and API Stability 23 | 24 | * Some degree of POSIX compliance is sought after, but this module is not always 25 | restricted by the standard. 26 | For example `posix.openlog()` also supports non-POSIX facility codes. 27 | * node-posix version format: *MAJOR.MINOR.PATCH* 28 | * *PATCH* version upgrades will not break backward compatibility, so it should be 29 | safe to upgrade from 1.0.0 to 1.0.1, 1.0.2, etc. 30 | * *MAJOR* and *MINOR* version upgrades *probably* break backward-compatibility in 31 | some way, which may or may not affect your module. 32 | * Recommended way to define a version dependency to node-posix in your 33 | `package.json`: `"dependencies": { "posix": "1.0.x" }` 34 | * Update the version in `package.json` manually after validating compatibility 35 | with new node-posix *MAJOR* and *MINOR* releases). 36 | * *"package.json dependencies done right"*: 37 | http://blog.nodejitsu.com/package-dependencies-done-right 38 | 39 | ## Related modules 40 | 41 | Other extension modules that provide POSIX/Unix/Linux/BSD functionality: 42 | 43 | * glob() http://npm.im/glob 44 | * getrusage() http://npm.im/getrusage 45 | * chroot(), daemonization http://npm.im/daemon-tools 46 | * iconv() http://npm.im/iconv 47 | * mmap() http://npm.im/mmap 48 | * PAM authentication, flock() and mkstemp() http://npm.im/unixlib 49 | * execvp() http://npm.im/kexec 50 | 51 | ## General Information 52 | 53 | ### User and Group ID Management 54 | * `posix.getgid()` is an alias to Node core `process.getgid()` 55 | * `posix.getuid()` is an alias to Node core `process.getuid()` 56 | * `posix.setgid()` is an alias to Node core `process.setgid()` 57 | * `posix.setuid()` is an alias to Node core `process.setuid()`, 58 | NOTE: should be used carefully due to inconsistent behavior under different 59 | operating systems, see http://www.cs.ucdavis.edu/~hchen/paper/usenix02.html 60 | 61 | ### Resource limits 62 | * `ulimit()` is obsolete, use `posix.setrlimit()` instead. 63 | 64 | ## General usage 65 | 66 | * Installation: `npm install posix` 67 | * In your code: `var posix = require('posix');` 68 | 69 | ## POSIX System Calls 70 | 71 | ### posix.chroot(path) 72 | 73 | Changes the root directory of the calling process to that specified in `path`. 74 | This directory will be used for pathnames beginning with `/`. The root 75 | directory is inherited by all children of the calling process. 76 | 77 | The working directory is also automatically set to the new root directory. 78 | 79 | NOTE: Please be aware of the limitations of `chroot` jails: 80 | 81 | * "Best Practices for UNIX `chroot()` Operations": 82 | http://www.unixwiz.net/techtips/chroot-practices.html 83 | * "How to break out of a `chroot()` jail": 84 | http://www.bpfh.net/simes/computing/chroot-break.html 85 | 86 | Example: 87 | 88 | posix.chroot('/somewhere/safe'); 89 | 90 | ### posix.getegid() 91 | 92 | Returns the current process's effective group ID. 93 | 94 | console.log('Effective GID: ' + posix.getegid()); 95 | 96 | ### posix.geteuid() 97 | 98 | Returns the current process's effective user ID. 99 | 100 | console.log('Effective UID: ' + posix.geteuid()); 101 | 102 | ### posix.getgrnam(group) 103 | 104 | Get the group database entry for the given group. `group` can be specified 105 | either as a numeric GID or a group name (string). 106 | 107 | var util = require('util'); 108 | util.inspect(posix.getgrnam('wheel')); 109 | 110 | Example output of above: 111 | 112 | { name: 'wheel', passwd: '*', gid: 0, members: [ 'root' ] } 113 | 114 | ### posix.getpgid(pid) 115 | 116 | Return the process group ID of the current process (`posix.getpgid(0)`) or of 117 | a process of a given PID (`posix.getpgid(PID)`). 118 | 119 | console.log('My PGID: ' + posix.getpgid(0)); 120 | console.log('init's PGID: ' + posix.getpgid(1)); 121 | 122 | ### posix.setpgid(pid) 123 | 124 | Return the process group ID of the current process (`posix.setpgid(0, PGID)`) 125 | or of a process of a given PID (`posix.getpgid(PID, PGID)`). 126 | 127 | // move process into it's own process group 128 | posix.setpgid(process.pid, process.pid) 129 | 130 | ### posix.getppid() 131 | 132 | Returns the parent process's PID. 133 | 134 | console.log('Parent PID: ' + posix.getppid()); 135 | 136 | ### posix.getpwnam(user) 137 | 138 | Get the user database entry for the given user. `user` can be specified either 139 | as a numeric UID or a username (string). 140 | 141 | var util = require('util'); 142 | util.inspect(posix.getpwnam('root')); 143 | 144 | Example output of above: 145 | 146 | { name: 'root', 147 | passwd: '*', 148 | uid: 0, 149 | gid: 0, 150 | gecos: 'System Administrator', 151 | shell: '/bin/sh', 152 | dir: '/var/root' } 153 | 154 | ### posix.getrlimit(resource) 155 | 156 | Get resource limits. (See getrlimit(2).) 157 | 158 | The `soft` limit is the value that the kernel enforces for the 159 | corresponding resource. The `hard` limit acts as a ceiling for the soft 160 | limit: an unprivileged process may only set its soft limit to a value in the 161 | range from 0 up to the hard limit, and (irreversibly) lower its hard limit. 162 | 163 | A limit value of `null` indicates "unlimited" (RLIM_INFINITY). 164 | 165 | Supported resources: 166 | 167 | `'core'` (RLIMIT_CORE) Maximum size of core file. When 0 no core dump files 168 | are created. 169 | 170 | `'cpu'` (RLIMIT_CPU) CPU time limit in seconds. When the process reaches the 171 | soft limit, it is sent a SIGXCPU signal. The default action for this signal is 172 | to terminate the process. 173 | 174 | `'data'` (RLIMIT_DATA) The maximum size of the process's data segment 175 | (initialized data, uninitialized data, and heap). 176 | 177 | `'fsize'` (RLIMIT_FSIZE) The maximum size of files that the process may create. 178 | Attempts to extend a file beyond this limit result in delivery of a SIGXFSZ 179 | signal. 180 | 181 | `'nofile'` (RLIMIT_NOFILE) Specifies a value one greater than the maximum file 182 | descriptor number that can be opened by this process. 183 | 184 | `'nproc'` (RLIMIT_NPROC) The maximum number of processes (or, more precisely on Linux, 185 | threads) that can be created by this process. *(Note: Only Linux, OS X, and BSDs support the 'nproc' resource limit. An error will be raised on unsupported platforms.)* 186 | 187 | `'stack'` (RLIMIT_STACK) The maximum size of the process stack, in bytes. Upon 188 | reaching this limit, a SIGSEGV signal is generated. 189 | 190 | `'as'` (RLIMIT_AS) The maximum size of the process's virtual memory (address 191 | space) in bytes. 192 | 193 | var limits = posix.getrlimit('nofile'); 194 | console.log('Current limits: soft=' + limits.soft + ', max=' + limits.hard); 195 | 196 | ### posix.initgroups(user, group) 197 | 198 | Sets the group access list to all groups of which user is a member. 199 | The additional group group is also added to the list. 200 | 201 | posix.initgroups("node", "httpd"); // all groups of 'node' plus 'httpd' 202 | 203 | ### posix.setegid(gid) 204 | 205 | Sets the Effective group ID of the current process. `gid` can be either a 206 | numeric GID or a group name (string). 207 | 208 | posix.setegid(0); // set effective group UID to "wheel" 209 | posix.setegid('nobody'); 210 | 211 | ### posix.seteuid(uid) 212 | 213 | Sets the Effective user ID of the current process. `uid` can be either a 214 | numeric UID or a username (string). 215 | 216 | posix.seteuid(0); // set effective UID to "root" 217 | posix.seteuid('nobody'); 218 | 219 | ### posix.setregid(rgid, egid) 220 | 221 | Sets the Real and Effective group IDs of the current process. `rgid` and `egid` 222 | can be either a numeric UIDs or group names (strings). A value of `-1` means 223 | that the corresponding GID is left unchanged. 224 | 225 | posix.setregid(-1, 1000); // just set the EGID to 1000 226 | posix.setregid('www-data', 'www-data'); // change both RGID and EGID to "www-data" 227 | 228 | ### posix.setreuid(ruid, euid) 229 | 230 | Sets the Real and Effective user IDs of the current process. `ruid` and `euid` 231 | can be either a numeric UIDs or usernames (strings). A value of `-1` means 232 | that the corresponding UID is left unchanged. 233 | 234 | IMPORTANT NOTE: what happens to the Saved UID when `setreuid()` is called is 235 | operating system dependent. For example on OSX the Saved UID seems to be set 236 | to the previous EUID. This means that the process can escape back to EUID=0 237 | simply by calling `setreuid(0, 0)`. A workaround for this is to call 238 | `posix.setreuid(ruid, euid)` twice with the same arguments. 239 | 240 | posix.setreuid(-1, 1000); // just set the EUID to 1000 241 | posix.setreuid('nobody', 'nobody'); // change both RUID and EUID to "nobody" 242 | 243 | ### posix.setrlimit(resource, limits) 244 | 245 | Set resource limits. (See setrlimit(2).) Supported resource types are listed 246 | under `posix.getrlimit`. 247 | 248 | The `limits` argument is an object in the form 249 | `{ soft: SOFT_LIMIT, hard: HARD_LIMIT }`. Current limit values are used if 250 | either `soft` or `hard` key is not specifing in the `limits` object. A limit 251 | value of `null` indicates "unlimited" (RLIM_INFINITY). 252 | 253 | // raise maximum number of open file descriptors to 10k, hard limit is left unchanged 254 | posix.setrlimit('nofile', { soft: 10000 }); 255 | 256 | // enable core dumps of unlimited size 257 | posix.setrlimit('core', { soft: null, hard: null }); 258 | 259 | ### posix.setsid() 260 | 261 | Creates a session and sets the process group ID. Returns the process group ID. 262 | 263 | console.log('Session ID: ' + posix.setsid()); 264 | 265 | ## Syslog 266 | 267 | ### posix.openlog(identity, options, facility) 268 | 269 | Open a connection to the logger. 270 | 271 | Arguments: 272 | 273 | * `identity` - defines the name of the process visible in the logged entries. 274 | * `options` - set of option flags (see below). 275 | * `facility` - facility code for the logged messages (see below). 276 | 277 | Options: 278 | 279 | * `'cons'` - Log to the system console on error. 280 | * `'ndelay'` - Connect to syslog daemon immediately. 281 | * `'nowait'` - Do not wait for child processes. 282 | * `'odelay'` - Delay open until syslog() is called. 283 | * `'pid'` - Log the process ID with each message. 284 | 285 | Facilities: 286 | 287 | NOTE: only `'user'` and `'local0'` .. `'local7'` are defined in the POSIX 288 | standard. However, the other codes should be pretty well supported on most 289 | platforms. 290 | 291 | * `'kern'` 292 | * `'user'` 293 | * `'mail'` 294 | * `'news'` 295 | * `'uucp'` 296 | * `'daemon'` 297 | * `'auth'` 298 | * `'cron'` 299 | * `'lpr'` 300 | * `'local0'` .. `'local7'` 301 | 302 | Example: 303 | 304 | posix.openlog('myprog', {odelay: true, pid: true}, 'local7'); 305 | 306 | ### posix.closelog() 307 | 308 | Close connection to the logger. 309 | 310 | ### posix.setlogmask(mask) 311 | 312 | Sets a priority mask for log messages. Further `posix.syslog()` messages are 313 | only sent out if their priority is included in the mask. Priorities are listed 314 | under `posix.syslog()`. 315 | 316 | // only send the most critical messages 317 | posix.setlogmask({emerg:true, alert: true, crit: true}); 318 | 319 | ### posix.syslog(priority, message) 320 | 321 | Send a message to the syslog logger using the given `priority`. 322 | 323 | Priorities: 324 | 325 | * `'emerg'` 326 | * `'alert'` 327 | * `'crit'` 328 | * `'err'` 329 | * `'warning'` 330 | * `'notice'` 331 | * `'info'` 332 | * `'debug'` 333 | 334 | Example: 335 | 336 | posix.syslog('info', 'hello, world!'); 337 | 338 | ## hostname 339 | 340 | ### posix.gethostname() 341 | 342 | Returns the hostname of the operating system. 343 | 344 | ### posix.sethostname(hostname) 345 | 346 | Sets the hostname of the operating system. 347 | 348 | Example: 349 | 350 | posix.sethostname('beefheart'); 351 | 352 | ## swap 353 | 354 | ### posix.swapon(path, swapflags) 355 | 356 | Enable the swap device located at `path`. `swapflags` is an optional array of 357 | strings to define the flags of the swap device, being available `prefer` and 358 | `discard`. 359 | 360 | ### posix.swapoff(path) 361 | 362 | Disable the swap device located at `path`. 363 | 364 | ## Credits 365 | 366 | * Some of the documentation strings stolen from Linux man pages. 367 | * `posix.seteuid` etc. implementation is based on Node core project `SetUid` 368 | * Thank you for your contributions: 369 | Igor Pashev, Dan Bornstein, Jamie Paton, Nick Muerdter, Oskari Saarenmaa, 370 | Theo Schlossnagle, Alex Potsides, Robey Pointer, Stephen Sugden, 371 | Dave Longley, Jesús Leganés-Combarro 372 | 373 | ## LICENSE 374 | 375 | Copyright (c) 2011-2015 Mika Eloranta 376 | 377 | Permission is hereby granted, free of charge, to any person obtaining a copy 378 | of this software and associated documentation files (the "Software"), to deal 379 | in the Software without restriction, including without limitation the rights 380 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 381 | copies of the Software, and to permit persons to whom the Software is 382 | furnished to do so, subject to the following conditions: 383 | 384 | The above copyright notice and this permission notice shall be included in 385 | all copies or substantial portions of the Software. 386 | 387 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 388 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 389 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 390 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 391 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 392 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 393 | THE SOFTWARE. 394 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "posix", 5 | "sources": [ "src/posix.cc" ], 6 | "include_dirs": ["", 19 | "main" : "./lib/posix", 20 | "dependencies" : { 21 | "nan": "^2.14.x" 22 | }, 23 | "scripts" : { 24 | "test" : "make test" 25 | }, 26 | "engines" : { "node": ">= 0.10.0" } 27 | } 28 | -------------------------------------------------------------------------------- /src/posix.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include // setrlimit, getrlimit 6 | #include // PATH_MAX 7 | #include // getpwnam, passwd 8 | #include // getgrnam, group 9 | #include // openlog, closelog, syslog, setlogmask 10 | 11 | #ifdef __linux__ 12 | # include // swapon, swapoff 13 | #endif 14 | 15 | using v8::Array; 16 | using v8::FunctionTemplate; 17 | using v8::Integer; 18 | using v8::Local; 19 | using v8::Number; 20 | using v8::Object; 21 | using v8::String; 22 | using v8::Value; 23 | 24 | NAN_METHOD(node_getppid) { 25 | Nan::HandleScope scope; 26 | 27 | if (info.Length() != 0) { 28 | return Nan::ThrowError("getppid: takes no arguments"); 29 | } 30 | 31 | // on some platforms pid_t is defined as long hence the static_cast 32 | info.GetReturnValue().Set(Nan::New(static_cast(getppid()))); 33 | } 34 | 35 | NAN_METHOD(node_getpgid) { 36 | Nan::HandleScope scope; 37 | 38 | if (info.Length() != 1) { 39 | return Nan::ThrowError("getpgid: takes exactly one argument"); 40 | } 41 | 42 | if (!info[0]->IsNumber()) { 43 | return Nan::ThrowTypeError("getpgid: first argument must be an integer"); 44 | } 45 | 46 | const pid_t pid = Nan::To(info[0]).ToLocalChecked()->Value(); 47 | // on some platforms pid_t is defined as long hence the static_cast 48 | info.GetReturnValue().Set(Nan::New(static_cast(getpgid(pid)))); 49 | } 50 | 51 | NAN_METHOD(node_setpgid) { 52 | Nan::HandleScope scope; 53 | 54 | if (info.Length() != 2) { 55 | return Nan::ThrowError("setpgid: takes exactly two arguments"); 56 | } 57 | 58 | if (!info[0]->IsNumber()) { 59 | return Nan::ThrowTypeError("setpgid: first argument must be an integer"); 60 | } 61 | 62 | if (!info[1]->IsNumber()) { 63 | return Nan::ThrowTypeError("setpgid: first argument must be an integer"); 64 | } 65 | 66 | if (setpgid(Nan::To(info[0]).ToLocalChecked()->Value(), Nan::To(info[1]).ToLocalChecked()->Value()) < 0) { 67 | return Nan::ThrowError(Nan::ErrnoException(errno, "setpgid", "")); 68 | } 69 | 70 | info.GetReturnValue().Set(Nan::Undefined()); 71 | } 72 | 73 | NAN_METHOD(node_geteuid) { 74 | Nan::HandleScope scope; 75 | 76 | if (info.Length() != 0) { 77 | return Nan::ThrowError("geteuid: takes no arguments"); 78 | } 79 | 80 | info.GetReturnValue().Set(Nan::New(geteuid())); 81 | } 82 | 83 | NAN_METHOD(node_getegid) { 84 | Nan::HandleScope scope; 85 | 86 | if (info.Length() != 0) { 87 | return Nan::ThrowError("getegid: takes no arguments"); 88 | } 89 | 90 | info.GetReturnValue().Set(Nan::New(getegid())); 91 | } 92 | 93 | NAN_METHOD(node_setsid) { 94 | Nan::HandleScope scope; 95 | 96 | if (info.Length() != 0) { 97 | return Nan::ThrowError("setsid: takes no arguments"); 98 | } 99 | 100 | pid_t sid = setsid(); 101 | 102 | if (sid == -1) { 103 | return Nan::ThrowError(Nan::ErrnoException(errno, "setsid", "")); 104 | } 105 | 106 | // on some platforms pid_t is defined as long hence the static_cast 107 | info.GetReturnValue().Set(Nan::New(static_cast(sid))); 108 | } 109 | 110 | NAN_METHOD(node_chroot) { 111 | Nan::HandleScope scope; 112 | 113 | if (info.Length() != 1) { 114 | return Nan::ThrowError("chroot: takes exactly one argument"); 115 | } 116 | 117 | if (!info[0]->IsString()) { 118 | return Nan::ThrowTypeError("chroot: first argument must be a string"); 119 | } 120 | 121 | Nan::Utf8String dir_path(info[0]); 122 | 123 | // proper order is to first chdir() and then chroot() 124 | if (chdir(*dir_path)) { 125 | return Nan::ThrowError(Nan::ErrnoException(errno, "chroot: chdir: ", "")); 126 | } 127 | 128 | if(chroot(*dir_path)) { 129 | return Nan::ThrowError(Nan::ErrnoException(errno, "chroot", "")); 130 | } 131 | 132 | info.GetReturnValue().Set(Nan::Undefined()); 133 | } 134 | 135 | struct name_to_int_t { 136 | const char* name; 137 | int resource; 138 | }; 139 | 140 | static const name_to_int_t rlimit_name_to_res[] = { 141 | { "core", RLIMIT_CORE }, 142 | { "cpu", RLIMIT_CPU }, 143 | { "data", RLIMIT_DATA }, 144 | { "fsize", RLIMIT_FSIZE }, 145 | { "nofile", RLIMIT_NOFILE }, 146 | #ifdef RLIMIT_NPROC 147 | { "nproc", RLIMIT_NPROC }, 148 | #endif 149 | { "stack", RLIMIT_STACK }, 150 | #ifdef RLIMIT_AS 151 | { "as", RLIMIT_AS }, 152 | #endif 153 | { 0, 0 } 154 | }; 155 | 156 | // return null if value is RLIM_INFINITY, otherwise the uint value 157 | static Local rlimit_value(rlim_t limit) { 158 | if (limit == RLIM_INFINITY) { 159 | return Nan::Null(); 160 | } else { 161 | return Nan::New((double)limit); 162 | } 163 | } 164 | 165 | NAN_METHOD(node_getrlimit) { 166 | Nan::HandleScope scope; 167 | 168 | if (info.Length() != 1) { 169 | return Nan::ThrowError("getrlimit: requires exactly one argument"); 170 | } 171 | 172 | if (!info[0]->IsString()) { 173 | return Nan::ThrowTypeError("getrlimit: argument must be a string"); 174 | } 175 | 176 | struct rlimit limit; 177 | Nan::Utf8String rlimit_name(info[0]); 178 | int resource = -1; 179 | 180 | for (const name_to_int_t* item = rlimit_name_to_res; item->name; ++item) { 181 | if (!strcmp(*rlimit_name, item->name)) { 182 | resource = item->resource; 183 | break; 184 | } 185 | } 186 | 187 | if (resource < 0) { 188 | return Nan::ThrowError("getrlimit: unknown resource name"); 189 | } 190 | 191 | if (getrlimit(resource, &limit)) { 192 | return Nan::ThrowError(Nan::ErrnoException(errno, "getrlimit", "")); 193 | } 194 | 195 | Local data = Nan::New(); 196 | Nan::Set(data, Nan::New("soft").ToLocalChecked(), rlimit_value(limit.rlim_cur)); 197 | Nan::Set(data, Nan::New("hard").ToLocalChecked(), rlimit_value(limit.rlim_max)); 198 | 199 | info.GetReturnValue().Set(data); 200 | } 201 | 202 | NAN_METHOD(node_setrlimit) { 203 | Nan::HandleScope scope; 204 | 205 | if (info.Length() != 2) { 206 | return Nan::ThrowError("setrlimit: requires exactly two arguments"); 207 | } 208 | 209 | if (!info[0]->IsString()) { 210 | return Nan::ThrowTypeError("setrlimit: argument 0 must be a string"); 211 | } 212 | 213 | if (!info[1]->IsObject()) { 214 | return Nan::ThrowTypeError("setrlimit: argument 1 must be an object"); 215 | } 216 | 217 | Nan::Utf8String rlimit_name(info[0]); 218 | int resource = -1; 219 | for (const name_to_int_t* item = rlimit_name_to_res; item->name; ++item) { 220 | if (!strcmp(*rlimit_name, item->name)) { 221 | resource = item->resource; 222 | break; 223 | } 224 | } 225 | 226 | if (resource < 0) { 227 | return Nan::ThrowError("setrlimit: unknown resource name"); 228 | } 229 | 230 | Local limit_in = Nan::To(info[1]).ToLocalChecked(); // Cast 231 | Local soft_key = Nan::New("soft").ToLocalChecked(); 232 | Local hard_key = Nan::New("hard").ToLocalChecked(); 233 | struct rlimit limit; 234 | bool get_soft = false, get_hard = false; 235 | if (Nan::Has(limit_in, soft_key).ToChecked()) { 236 | if (Nan::Get(limit_in, soft_key).ToLocalChecked()->IsNull()) { 237 | limit.rlim_cur = RLIM_INFINITY; 238 | } else { 239 | limit.rlim_cur = Nan::To(Nan::Get(limit_in, soft_key).ToLocalChecked()).ToLocalChecked()->Value(); 240 | } 241 | } else { 242 | get_soft = true; 243 | } 244 | 245 | if (Nan::Has(limit_in, hard_key).ToChecked()) { 246 | if (Nan::Get(limit_in, hard_key).ToLocalChecked()->IsNull()) { 247 | limit.rlim_max = RLIM_INFINITY; 248 | } else { 249 | limit.rlim_max = Nan::To(Nan::Get(limit_in, hard_key).ToLocalChecked()).ToLocalChecked()->Value(); 250 | } 251 | } else { 252 | get_hard = true; 253 | } 254 | 255 | if (get_soft || get_hard) { 256 | // current values for the limits are needed 257 | struct rlimit current; 258 | if (getrlimit(resource, ¤t)) { 259 | return Nan::ThrowError(Nan::ErrnoException(errno, "getrlimit", "")); 260 | } 261 | if (get_soft) { limit.rlim_cur = current.rlim_cur; } 262 | if (get_hard) { limit.rlim_max = current.rlim_max; } 263 | } 264 | 265 | if (setrlimit(resource, &limit)) { 266 | return Nan::ThrowError(Nan::ErrnoException(errno, "setrlimit", "")); 267 | } 268 | 269 | info.GetReturnValue().Set(Nan::Undefined()); 270 | } 271 | 272 | NAN_METHOD(node_getpwnam) { 273 | Nan::HandleScope scope; 274 | 275 | if (info.Length() != 1) { 276 | return Nan::ThrowError("getpwnam: requires exactly 1 argument"); 277 | } 278 | 279 | struct passwd* pwd; 280 | errno = 0; // reset errno before the call 281 | 282 | if (info[0]->IsNumber()) { 283 | pwd = getpwuid(Nan::To(info[0]).ToLocalChecked()->Value()); 284 | if (errno) { 285 | return Nan::ThrowError(Nan::ErrnoException(errno, "getpwuid", "")); 286 | } 287 | } else if (info[0]->IsString()) { 288 | Nan::Utf8String pwnam(info[0]); 289 | pwd = getpwnam(*pwnam); 290 | if(errno) { 291 | return Nan::ThrowError(Nan::ErrnoException(errno, "getpwnam", "")); 292 | } 293 | } else { 294 | return Nan::ThrowTypeError("argument must be a number or a string"); 295 | } 296 | 297 | if (!pwd) { 298 | return Nan::ThrowError("user id does not exist"); 299 | } 300 | 301 | Local obj = Nan::New(); 302 | Nan::Set(obj, Nan::New("name").ToLocalChecked(), Nan::New(pwd->pw_name).ToLocalChecked()); 303 | Nan::Set(obj, Nan::New("passwd").ToLocalChecked(), Nan::New(pwd->pw_passwd).ToLocalChecked()); 304 | Nan::Set(obj, Nan::New("uid").ToLocalChecked(), Nan::New(pwd->pw_uid)); 305 | Nan::Set(obj, Nan::New("gid").ToLocalChecked(), Nan::New(pwd->pw_gid)); 306 | #ifdef __ANDROID__ 307 | Nan::Set(obj, Nan::New("gecos").ToLocalChecked(), Nan::Null()); 308 | #else 309 | Nan::Set(obj, Nan::New("gecos").ToLocalChecked(), Nan::New(pwd->pw_gecos).ToLocalChecked()); 310 | #endif 311 | Nan::Set(obj, Nan::New("shell").ToLocalChecked(), Nan::New(pwd->pw_shell).ToLocalChecked()); 312 | Nan::Set(obj, Nan::New("dir").ToLocalChecked(), Nan::New(pwd->pw_dir).ToLocalChecked()); 313 | 314 | info.GetReturnValue().Set(obj); 315 | } 316 | 317 | NAN_METHOD(node_getgrnam) { 318 | Nan::HandleScope scope; 319 | 320 | if (info.Length() != 1) { 321 | return Nan::ThrowError("getgrnam: requires exactly 1 argument"); 322 | } 323 | 324 | struct group* grp; 325 | errno = 0; // reset errno before the call 326 | 327 | if (info[0]->IsNumber()) { 328 | grp = getgrgid(Nan::To(info[0]).ToLocalChecked()->Value()); 329 | if (errno) { 330 | return Nan::ThrowError(Nan::ErrnoException(errno, "getgrgid", "")); 331 | } 332 | } else if (info[0]->IsString()) { 333 | Nan::Utf8String pwnam(info[0]); 334 | grp = getgrnam(*pwnam); 335 | if (errno) { 336 | return Nan::ThrowError(Nan::ErrnoException(errno, "getgrnam", "")); 337 | } 338 | } else { 339 | return Nan::ThrowTypeError("argument must be a number or a string"); 340 | } 341 | 342 | if (!grp) { 343 | return Nan::ThrowError("group id does not exist"); 344 | } 345 | 346 | Local obj = Nan::New(); 347 | Nan::Set(obj, Nan::New("name").ToLocalChecked(), Nan::New(grp->gr_name).ToLocalChecked()); 348 | Nan::Set(obj, Nan::New("passwd").ToLocalChecked(), Nan::New(grp->gr_passwd).ToLocalChecked()); 349 | Nan::Set(obj, Nan::New("gid").ToLocalChecked(), Nan::New(grp->gr_gid)); 350 | 351 | Local members = Nan::New(); 352 | char** cur = grp->gr_mem; 353 | for (size_t i=0; *cur; ++i, ++cur) { 354 | Nan::Set(members, i, Nan::New(*cur).ToLocalChecked()); 355 | } 356 | Nan::Set(obj, Nan::New("members").ToLocalChecked(), members); 357 | 358 | info.GetReturnValue().Set(obj); 359 | } 360 | 361 | NAN_METHOD(node_initgroups) { 362 | Nan::HandleScope scope; 363 | 364 | if (info.Length() != 2) { 365 | return Nan::ThrowError("initgroups: requires exactly 2 arguments"); 366 | } 367 | 368 | if (!info[0]->IsString() || !info[1]->IsNumber()) { 369 | return Nan::ThrowTypeError("initgroups: first argument must be a string " 370 | " and the second an integer"); 371 | } 372 | 373 | Nan::Utf8String unam(info[0]); 374 | if (initgroups(*unam, Nan::To(info[1]).ToLocalChecked()->Value())) { 375 | return Nan::ThrowError(Nan::ErrnoException(errno, "initgroups", "")); 376 | } 377 | 378 | info.GetReturnValue().Set(Nan::Undefined()); 379 | } 380 | 381 | NAN_METHOD(node_seteuid) { 382 | Nan::HandleScope scope; 383 | 384 | if (info.Length() != 1) { 385 | return Nan::ThrowError("seteuid: requires exactly 1 argument"); 386 | } 387 | 388 | if (seteuid(Nan::To(info[0]).ToLocalChecked()->Value())) { 389 | return Nan::ThrowError(Nan::ErrnoException(errno, "seteuid", "")); 390 | } 391 | 392 | info.GetReturnValue().Set(Nan::Undefined()); 393 | } 394 | 395 | NAN_METHOD(node_setegid) { 396 | Nan::HandleScope scope; 397 | 398 | if (info.Length() != 1) { 399 | return Nan::ThrowError("setegid: requires exactly 1 argument"); 400 | } 401 | 402 | if (setegid(Nan::To(info[0]).ToLocalChecked()->Value())) { 403 | return Nan::ThrowError(Nan::ErrnoException(errno, "setegid", "")); 404 | } 405 | 406 | info.GetReturnValue().Set(Nan::Undefined()); 407 | } 408 | 409 | NAN_METHOD(node_setregid) { 410 | Nan::HandleScope scope; 411 | 412 | if (info.Length() != 2) { 413 | return Nan::ThrowError("setregid: requires exactly 2 arguments"); 414 | } 415 | 416 | if (setregid(Nan::To(info[0]).ToLocalChecked()->Value(), Nan::To(info[1]).ToLocalChecked()->Value())) { 417 | return Nan::ThrowError(Nan::ErrnoException(errno, "setregid", "")); 418 | } 419 | 420 | info.GetReturnValue().Set(Nan::Undefined()); 421 | } 422 | 423 | NAN_METHOD(node_setreuid) { 424 | Nan::HandleScope scope; 425 | 426 | if (info.Length() != 2) { 427 | return Nan::ThrowError("setreuid: requires exactly 2 arguments"); 428 | } 429 | 430 | if (setreuid(Nan::To(info[0]).ToLocalChecked()->Value(), Nan::To(info[1]).ToLocalChecked()->Value())) { 431 | return Nan::ThrowError(Nan::ErrnoException(errno, "setreuid", "")); 432 | } 433 | 434 | info.GetReturnValue().Set(Nan::Undefined()); 435 | } 436 | 437 | // openlog() first argument (const char* ident) is not guaranteed to be 438 | // copied within the openlog() call so we need to keep it in a safe location 439 | static const size_t MAX_SYSLOG_IDENT=100; 440 | static char syslog_ident[MAX_SYSLOG_IDENT+1] = {0}; 441 | 442 | NAN_METHOD(node_openlog) { 443 | Nan::HandleScope scope; 444 | 445 | if (info.Length() != 3) { 446 | return Nan::ThrowError("openlog: requires exactly 3 arguments"); 447 | } 448 | 449 | Nan::Utf8String ident(info[0]); 450 | strncpy(syslog_ident, *ident, MAX_SYSLOG_IDENT); 451 | syslog_ident[MAX_SYSLOG_IDENT] = 0; 452 | if (!info[1]->IsNumber() || !info[2]->IsNumber()) { 453 | return Nan::ThrowError("openlog: invalid argument values"); 454 | } 455 | // note: openlog does not ever fail, no return value 456 | openlog(syslog_ident, Nan::To(info[1]).ToLocalChecked()->Value(), Nan::To(info[2]).ToLocalChecked()->Value()); 457 | 458 | info.GetReturnValue().Set(Nan::Undefined()); 459 | } 460 | 461 | NAN_METHOD(node_closelog) { 462 | Nan::HandleScope scope; 463 | 464 | if (info.Length() != 0) { 465 | return Nan::ThrowError("closelog: does not take any arguments"); 466 | } 467 | 468 | // note: closelog does not ever fail, no return value 469 | closelog(); 470 | 471 | info.GetReturnValue().Set(Nan::Undefined()); 472 | } 473 | 474 | NAN_METHOD(node_syslog) { 475 | Nan::HandleScope scope; 476 | 477 | if (info.Length() != 2) { 478 | return Nan::ThrowError("syslog: requires exactly 2 arguments"); 479 | } 480 | 481 | Nan::Utf8String message(info[1]); 482 | // note: syslog does not ever fail, no return value 483 | syslog(Nan::To(info[0]).ToLocalChecked()->Value(), "%s", *message); 484 | 485 | info.GetReturnValue().Set(Nan::Undefined()); 486 | } 487 | 488 | NAN_METHOD(node_setlogmask) { 489 | Nan::HandleScope scope; 490 | 491 | if (info.Length() != 1) { 492 | return Nan::ThrowError("setlogmask: takes exactly 1 argument"); 493 | } 494 | 495 | info.GetReturnValue().Set(Nan::New(setlogmask(Nan::To(info[0]).ToLocalChecked()->Value()))); 496 | } 497 | 498 | #define ADD_MASK_FLAG(name, flag) \ 499 | Nan::Set(obj, Nan::New(name).ToLocalChecked(), Nan::New(flag)); \ 500 | Nan::Set(obj, Nan::New("mask_" name).ToLocalChecked(), Nan::New(LOG_MASK(flag))); 501 | 502 | NAN_METHOD(node_update_syslog_constants) { 503 | Nan::HandleScope scope; 504 | 505 | if (info.Length() != 1) { 506 | return Nan::ThrowError("update_syslog_constants: takes exactly 1 argument"); 507 | } 508 | 509 | if (!info[0]->IsObject()) { 510 | return Nan::ThrowTypeError("update_syslog_constants: argument must be an object"); 511 | } 512 | 513 | Local obj = Nan::To(info[0]).ToLocalChecked(); 514 | ADD_MASK_FLAG("emerg", LOG_EMERG); 515 | ADD_MASK_FLAG("alert", LOG_ALERT); 516 | ADD_MASK_FLAG("crit", LOG_CRIT); 517 | ADD_MASK_FLAG("err", LOG_ERR); 518 | ADD_MASK_FLAG("warning", LOG_WARNING); 519 | ADD_MASK_FLAG("notice", LOG_NOTICE); 520 | ADD_MASK_FLAG("info", LOG_INFO); 521 | ADD_MASK_FLAG("debug", LOG_DEBUG); 522 | 523 | // facility constants 524 | Nan::Set(obj, Nan::New("auth").ToLocalChecked(), Nan::New(LOG_AUTH)); 525 | #ifdef LOG_AUTHPRIV 526 | Nan::Set(obj, Nan::New("authpriv").ToLocalChecked(), Nan::New(LOG_AUTHPRIV)); 527 | #endif 528 | Nan::Set(obj, Nan::New("cron").ToLocalChecked(), Nan::New(LOG_CRON)); 529 | Nan::Set(obj, Nan::New("daemon").ToLocalChecked(), Nan::New(LOG_DAEMON)); 530 | #ifdef LOG_FTP 531 | Nan::Set(obj, Nan::New("ftp").ToLocalChecked(), Nan::New(LOG_FTP)); 532 | #endif 533 | Nan::Set(obj, Nan::New("kern").ToLocalChecked(), Nan::New(LOG_KERN)); 534 | Nan::Set(obj, Nan::New("lpr").ToLocalChecked(), Nan::New(LOG_LPR)); 535 | Nan::Set(obj, Nan::New("mail").ToLocalChecked(), Nan::New(LOG_MAIL)); 536 | Nan::Set(obj, Nan::New("news").ToLocalChecked(), Nan::New(LOG_NEWS)); 537 | Nan::Set(obj, Nan::New("syslog").ToLocalChecked(), Nan::New(LOG_SYSLOG)); 538 | Nan::Set(obj, Nan::New("user").ToLocalChecked(), Nan::New(LOG_USER)); 539 | Nan::Set(obj, Nan::New("uucp").ToLocalChecked(), Nan::New(LOG_UUCP)); 540 | Nan::Set(obj, Nan::New("local0").ToLocalChecked(), Nan::New(LOG_LOCAL0)); 541 | Nan::Set(obj, Nan::New("local1").ToLocalChecked(), Nan::New(LOG_LOCAL1)); 542 | Nan::Set(obj, Nan::New("local2").ToLocalChecked(), Nan::New(LOG_LOCAL2)); 543 | Nan::Set(obj, Nan::New("local3").ToLocalChecked(), Nan::New(LOG_LOCAL3)); 544 | Nan::Set(obj, Nan::New("local4").ToLocalChecked(), Nan::New(LOG_LOCAL4)); 545 | Nan::Set(obj, Nan::New("local5").ToLocalChecked(), Nan::New(LOG_LOCAL5)); 546 | Nan::Set(obj, Nan::New("local6").ToLocalChecked(), Nan::New(LOG_LOCAL6)); 547 | Nan::Set(obj, Nan::New("local7").ToLocalChecked(), Nan::New(LOG_LOCAL7)); 548 | 549 | // option constants 550 | Nan::Set(obj, Nan::New("pid").ToLocalChecked(), Nan::New(LOG_PID)); 551 | Nan::Set(obj, Nan::New("cons").ToLocalChecked(), Nan::New(LOG_CONS)); 552 | Nan::Set(obj, Nan::New("ndelay").ToLocalChecked(), Nan::New(LOG_NDELAY)); 553 | Nan::Set(obj, Nan::New("odelay").ToLocalChecked(), Nan::New(LOG_ODELAY)); 554 | Nan::Set(obj, Nan::New("nowait").ToLocalChecked(), Nan::New(LOG_NOWAIT)); 555 | 556 | info.GetReturnValue().Set(Nan::Undefined()); 557 | } 558 | 559 | NAN_METHOD(node_gethostname) { 560 | Nan::HandleScope scope; 561 | 562 | if (info.Length() != 0) { 563 | return Nan::ThrowError("gethostname: takes no arguments"); 564 | } 565 | #ifndef HOST_NAME_MAX 566 | # define HOST_NAME_MAX 255 567 | #endif 568 | 569 | char hostname[HOST_NAME_MAX]; 570 | 571 | int rc = gethostname(hostname, HOST_NAME_MAX); 572 | if (rc != 0) { 573 | return Nan::ThrowError(Nan::ErrnoException(errno, "gethostname", "")); 574 | } 575 | 576 | info.GetReturnValue().Set(Nan::New(hostname).ToLocalChecked()); 577 | } 578 | 579 | #ifndef __ANDROID__ 580 | NAN_METHOD(node_sethostname) { 581 | Nan::HandleScope scope; 582 | 583 | if (info.Length() != 1) { 584 | return Nan::ThrowError("sethostname: takes exactly 1 argument"); 585 | } 586 | 587 | if (!info[0]->IsString()) { 588 | return Nan::ThrowTypeError("sethostname: first argument must be a string"); 589 | } 590 | 591 | Nan::Utf8String str(info[0]); 592 | 593 | int rc = sethostname(*str, str.length()); 594 | if (rc != 0) { 595 | return Nan::ThrowError(Nan::ErrnoException(errno, "sethostname", "")); 596 | } 597 | 598 | info.GetReturnValue().Set(Nan::Undefined()); 599 | } 600 | #endif // __ANDROID__ 601 | 602 | #ifdef __linux__ 603 | NAN_METHOD(node_swapon) { 604 | Nan::HandleScope scope; 605 | 606 | if (info.Length() != 2) { 607 | return Nan::ThrowError("swapon: takes exactly 2 argument"); 608 | } 609 | 610 | if (!info[0]->IsString()) { 611 | return Nan::ThrowTypeError("swapon: first argument must be a string"); 612 | } 613 | 614 | if (!info[1]->IsNumber()) { 615 | return Nan::ThrowTypeError("swapon: second argument must be an integer"); 616 | } 617 | 618 | Nan::Utf8String str(info[0]); 619 | 620 | int rc = swapon(*str, Nan::To(info[1]).ToLocalChecked()->Value()); 621 | if (rc != 0) { 622 | return Nan::ThrowError(Nan::ErrnoException(errno, "swapon", "")); 623 | } 624 | 625 | info.GetReturnValue().Set(Nan::Undefined()); 626 | } 627 | 628 | NAN_METHOD(node_swapoff) { 629 | Nan::HandleScope scope; 630 | 631 | if (info.Length() != 1) { 632 | return Nan::ThrowError("swapoff: takes exactly 1 argument"); 633 | } 634 | 635 | if (!info[0]->IsString()) { 636 | return Nan::ThrowTypeError("swapoff: first argument must be a string"); 637 | } 638 | 639 | Nan::Utf8String str(info[0]); 640 | 641 | int rc = swapoff(*str); 642 | if (rc != 0) { 643 | return Nan::ThrowError(Nan::ErrnoException(errno, "swapoff", "")); 644 | } 645 | 646 | info.GetReturnValue().Set(Nan::Undefined()); 647 | } 648 | 649 | NAN_METHOD(node_update_swap_constants) { 650 | Nan::HandleScope scope; 651 | 652 | if (info.Length() != 1) { 653 | return Nan::ThrowError("update_syslog_constants: takes exactly 1 argument"); 654 | } 655 | 656 | if (!info[0]->IsObject()) { 657 | return Nan::ThrowTypeError("update_syslog_constants: argument must be an object"); 658 | } 659 | 660 | Local obj = Nan::To(info[0]).ToLocalChecked(); 661 | Nan::Set(obj, Nan::New("prefer").ToLocalChecked(), Nan::New(SWAP_FLAG_PREFER)); 662 | #ifdef SWAP_FLAG_DISCARD 663 | Nan::Set(obj, Nan::New("discard").ToLocalChecked(), Nan::New(SWAP_FLAG_DISCARD)); 664 | #endif // SWAP_FLAG_DISCARD 665 | 666 | info.GetReturnValue().Set(Nan::Undefined()); 667 | } 668 | #endif // __linux__ 669 | 670 | #define EXPORT(name, symbol) Nan::Set(exports, \ 671 | Nan::New(name).ToLocalChecked(), \ 672 | Nan::GetFunction(Nan::New(symbol)).ToLocalChecked() \ 673 | ) 674 | 675 | void init(Local exports) { 676 | EXPORT("getppid", node_getppid); 677 | EXPORT("getpgid", node_getpgid); 678 | EXPORT("setpgid", node_setpgid); 679 | EXPORT("geteuid", node_geteuid); 680 | EXPORT("getegid", node_getegid); 681 | EXPORT("setsid", node_setsid); 682 | EXPORT("chroot", node_chroot); 683 | EXPORT("getrlimit", node_getrlimit); 684 | EXPORT("setrlimit", node_setrlimit); 685 | EXPORT("getpwnam", node_getpwnam); 686 | EXPORT("getgrnam", node_getgrnam); 687 | EXPORT("initgroups", node_initgroups); 688 | EXPORT("seteuid", node_seteuid); 689 | EXPORT("setegid", node_setegid); 690 | EXPORT("setregid", node_setregid); 691 | EXPORT("setreuid", node_setreuid); 692 | EXPORT("openlog", node_openlog); 693 | EXPORT("closelog", node_closelog); 694 | EXPORT("syslog", node_syslog); 695 | EXPORT("setlogmask", node_setlogmask); 696 | EXPORT("update_syslog_constants", node_update_syslog_constants); 697 | EXPORT("gethostname", node_gethostname); 698 | #ifndef __ANDROID__ 699 | EXPORT("sethostname", node_sethostname); 700 | #endif // __ANDROID__ 701 | 702 | #ifdef __linux__ 703 | EXPORT("swapon", node_swapon); 704 | EXPORT("swapoff", node_swapoff); 705 | EXPORT("update_swap_constants", node_update_swap_constants); 706 | #endif 707 | } 708 | 709 | NODE_MODULE(posix, init); 710 | -------------------------------------------------------------------------------- /test/unit/test-chroot.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | fs = require('fs'), 3 | posix = require("../../lib/posix"); 4 | 5 | function test_chroot() { 6 | assert.throws(function () { 7 | posix.chroot('/path/does/not/exist'); 8 | }, /ENOENT/); 9 | 10 | console.log("chroot to: " + __dirname); 11 | posix.chroot(__dirname); 12 | console.log("contents of root dir after chroot: " + fs.readdirSync("/")); 13 | var this_file_in_chroot = fs.statSync("/test-chroot.js"); 14 | assert.ok(this_file_in_chroot); 15 | assert.ok(this_file_in_chroot.isFile()); 16 | } 17 | 18 | if(process.getuid() == 0) { 19 | test_chroot() 20 | } 21 | else { 22 | console.log("warning: chroot tests skipped - not a privileged user!"); 23 | } 24 | -------------------------------------------------------------------------------- /test/unit/test-getegid.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | posix = require("../../lib/posix"); 3 | 4 | assert.throws(function() { 5 | posix.getegid(123) 6 | }, /takes no arguments/); 7 | 8 | var gid = posix.getegid(); 9 | console.log("getegid: " + gid); 10 | assert.equal(gid, process.getgid()); 11 | -------------------------------------------------------------------------------- /test/unit/test-geteuid.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | posix = require("../../lib/posix"); 3 | 4 | assert.throws(function() { 5 | posix.geteuid(123) 6 | }, /takes no arguments/); 7 | 8 | var uid = posix.geteuid(); 9 | console.log("geteuid: " + uid); 10 | assert.equal(uid, process.getuid()); 11 | -------------------------------------------------------------------------------- /test/unit/test-getgrnam.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | posix = require("../../lib/posix"); 3 | 4 | assert.throws(function() { 5 | posix.getgrnam(); 6 | }, /requires exactly 1 argument/); 7 | 8 | assert.throws(function() { 9 | posix.getgrnam(1, 2); 10 | }, /requires exactly 1 argument/); 11 | 12 | assert.throws(function() { 13 | posix.getgrnam("doesnotexistzzz123"); 14 | }, /group id does not exist/); 15 | 16 | assert.throws(function() { 17 | posix.getgrnam(65432); 18 | }, /group id does not exist/); 19 | 20 | var entry = posix.getgrnam("daemon"); 21 | console.log("getgrnam: " + JSON.stringify(entry)); 22 | assert.equal(entry.name, "daemon"); 23 | assert.equal(typeof(entry.gid), "number"); 24 | assert.equal(typeof(entry.passwd), "string"); 25 | 26 | 27 | assert.equal(posix.getgrnam(entry.gid).name, "daemon"); 28 | -------------------------------------------------------------------------------- /test/unit/test-gethostname.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var posix = require('../../lib/posix'); 3 | var os = require('os'); 4 | 5 | var hostname = posix.gethostname(); 6 | console.log('hostname: ' + hostname); 7 | assert.ok(hostname === os.hostname()); 8 | -------------------------------------------------------------------------------- /test/unit/test-getppid.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var posix = require("../../lib/posix"); 3 | 4 | var ppid = posix.getppid(); 5 | console.log("getppid: " + ppid); 6 | assert.ok(ppid > 1); 7 | -------------------------------------------------------------------------------- /test/unit/test-getpwnam.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | posix = require("../../lib/posix"); 3 | 4 | assert.throws(function() { 5 | posix.getpwnam(); 6 | }, /requires exactly 1 argument/); 7 | 8 | assert.throws(function() { 9 | posix.getpwnam(1, 2); 10 | }, /requires exactly 1 argument/); 11 | 12 | assert.throws(function() { 13 | posix.getpwnam("doesnotexistzzz123"); 14 | }, /user id does not exist/); 15 | 16 | assert.throws(function() { 17 | posix.getpwnam(65432); 18 | }, /user id does not exist/); 19 | 20 | var entry = posix.getpwnam("root"); 21 | console.log("getpwnam: " + JSON.stringify(entry)); 22 | assert.equal(entry.name, "root"); 23 | assert.equal(entry.uid, 0); 24 | assert.equal(entry.gid, 0); 25 | assert.equal(typeof(entry.gecos), "string"); 26 | assert.equal(typeof(entry.dir), "string"); 27 | assert.equal(typeof(entry.shell), "string"); 28 | 29 | assert.equal(posix.getpwnam(0).name, "root"); 30 | -------------------------------------------------------------------------------- /test/unit/test-getrlimit.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var posix = require('../../lib/posix'); 3 | 4 | var limits = [ 5 | "core", 6 | "cpu", 7 | "data", 8 | "fsize", 9 | "nofile", 10 | "stack", 11 | "as" 12 | ]; 13 | 14 | var unsupportedLimits = []; 15 | 16 | if(['linux', 'darwin', 'freebsd'].indexOf(process.platform) !== -1) { 17 | limits.push('nproc'); 18 | } else { 19 | unsupportedLimits.push('nproc'); 20 | } 21 | 22 | // getrlimit: invalid input args 23 | try { 24 | posix.getrlimit("foobar"); 25 | assert.ok(false); 26 | } 27 | catch(e) { } 28 | 29 | try { 30 | posix.getrlimit(); 31 | assert.ok(false); 32 | } 33 | catch(e) { } 34 | 35 | try { 36 | posix.getrlimit(0); 37 | assert.ok(false); 38 | } 39 | catch(e) { } 40 | 41 | // getrlimit: check all supported resources 42 | for(var i in limits) { 43 | var limit = posix.getrlimit(limits[i]); 44 | console.log("getrlimit " + limits[i] + ": " + JSON.stringify(limit)); 45 | assert.equal(true, typeof limit.soft == 'number' || limit.soft === null); 46 | assert.equal(true, typeof limit.hard == 'number' || limit.hard === null); 47 | } 48 | 49 | for(i in unsupportedLimits) { 50 | try { 51 | posix.getrlimit(unsupportedLimits[i]); 52 | assert.ok(false); 53 | } 54 | catch(e) { } 55 | } 56 | -------------------------------------------------------------------------------- /test/unit/test-initgroups.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | posix = require("../../lib/posix"); 3 | 4 | function test_initgroups() { 5 | assert.throws(function () { 6 | posix.initgroups("root", "dummyzzz1234"); 7 | }, /group id does not exist|ENOENT/); 8 | 9 | assert.throws(function () { 10 | posix.setregid("dummyzzz1234", "root"); 11 | }, /group id does not exist|ENOENT/); 12 | 13 | posix.initgroups("root", 0); 14 | } 15 | 16 | if('initgroups' in posix && posix.getuid() === 0) { 17 | test_initgroups() 18 | } 19 | else { 20 | console.log("warning: initgroups tests skipped - initgroups not supported or not a privileged user!"); 21 | } 22 | -------------------------------------------------------------------------------- /test/unit/test-setegid.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | posix = require("../../lib/posix"); 3 | 4 | assert.throws(function () { 5 | posix.setegid("dummyzzz1234"); 6 | }, /group id does not exist|ENOENT/); 7 | 8 | function test_setegid() { 9 | var old = posix.getegid(); 10 | assert.equal(old, 0); 11 | 12 | posix.setegid("daemon"); 13 | assert.equal(posix.getegid(), 1); 14 | 15 | posix.setegid(0); 16 | assert.equal(posix.getegid(), 0); 17 | 18 | posix.setegid(123); 19 | assert.equal(posix.getegid(), 123); 20 | 21 | posix.setegid(0); 22 | assert.equal(posix.getegid(), 0); 23 | } 24 | 25 | if(process.getuid() == 0) { 26 | test_setegid() 27 | } 28 | else { 29 | console.log("warning: setegid tests skipped - not a privileged user!"); 30 | } 31 | -------------------------------------------------------------------------------- /test/unit/test-seteuid.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | posix = require("../../lib/posix"); 3 | 4 | assert.throws(function () { 5 | posix.seteuid("dummyzzz1234"); 6 | }, /user id does not exist|ENOENT/); 7 | 8 | function test_seteuid() { 9 | var old = posix.geteuid(); 10 | assert.equal(old, 0); 11 | 12 | posix.seteuid("root"); 13 | assert.equal(posix.geteuid(), 0); 14 | 15 | posix.seteuid(0); 16 | assert.equal(posix.geteuid(), 0); 17 | 18 | posix.seteuid(123); 19 | assert.equal(posix.geteuid(), 123); 20 | 21 | assert.throws(function () { 22 | posix.seteuid(456); 23 | }, /EPERM/); 24 | 25 | posix.seteuid(0); 26 | assert.equal(posix.geteuid(), 0); 27 | } 28 | 29 | if(process.getuid() == 0) { 30 | test_seteuid() 31 | } 32 | else { 33 | console.log("warning: seteuid tests skipped - not a privileged user!"); 34 | } 35 | -------------------------------------------------------------------------------- /test/unit/test-setpgid.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | posix = require("../../lib/posix"); 3 | 4 | assert.throws(function () { 5 | posix.setpgid() 6 | }, /exactly two arguments/) 7 | 8 | assert.throws(function () { 9 | posix.setpgid('a', 1) 10 | }, /must be an integer/) 11 | 12 | assert.throws(function () { 13 | posix.setpgid(1, 'a') 14 | }, /must be an integer/) 15 | 16 | var old = posix.getpgid(0); 17 | assert.ok(old !== process.id); 18 | 19 | posix.setpgid(0, process.pid); 20 | assert.equal(posix.getpgid(0), process.pid); 21 | -------------------------------------------------------------------------------- /test/unit/test-setregid.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | posix = require("../../lib/posix"); 3 | 4 | assert.throws(function () { 5 | posix.setregid("dummyzzz1234", -1); 6 | }, /group id does not exist|ENOENT/); 7 | 8 | assert.throws(function () { 9 | posix.setregid(-1, "dummyzzz1234"); 10 | }, /group id does not exist|ENOENT/); 11 | 12 | function test_setregid() { 13 | var old_gid = posix.getegid(); 14 | assert.equal(old_gid, 0); 15 | 16 | posix.setregid(-1, -1); // NOP 17 | assert.equal(posix.getgid(), 0); 18 | assert.equal(posix.getegid(), 0); 19 | 20 | posix.setregid(0, 0); 21 | assert.equal(posix.getuid(), 0); 22 | assert.equal(posix.getegid(), 0); 23 | 24 | posix.setregid(0, 2); 25 | assert.equal(posix.getgid(), 0); 26 | assert.equal(posix.getegid(), 2); 27 | 28 | posix.setregid("daemon", "daemon"); 29 | assert.equal(posix.getgid(), 1); 30 | assert.equal(posix.getegid(), 1); 31 | 32 | posix.setregid(123, 456); 33 | assert.equal(posix.getgid(), 123); 34 | assert.equal(posix.getegid(), 456); 35 | } 36 | 37 | if(posix.getuid() == 0) { 38 | test_setregid() 39 | } 40 | else { 41 | console.log("warning: setregid tests skipped - not a privileged user!"); 42 | } 43 | -------------------------------------------------------------------------------- /test/unit/test-setreuid.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | posix = require("../../lib/posix"); 3 | 4 | assert.throws(function () { 5 | posix.setreuid("dummyzzz1234", -1); 6 | }, /user id does not exist|ENOENT/); 7 | 8 | assert.throws(function () { 9 | posix.setreuid(-1, "dummyzzz1234"); 10 | }, /user id does not exist|ENOENT/); 11 | 12 | function test_setreuid() { 13 | var old_ruid = posix.getuid(); 14 | assert.equal(old_ruid, 0); 15 | var old_euid = posix.geteuid(); 16 | assert.equal(old_euid, 0); 17 | 18 | posix.setreuid(-1, -1); // NOP 19 | assert.equal(posix.getuid(), 0); 20 | assert.equal(posix.geteuid(), 0); 21 | 22 | posix.setreuid("root", "root"); 23 | assert.equal(posix.getuid(), 0); 24 | assert.equal(posix.geteuid(), 0); 25 | 26 | posix.setreuid(-1, 2); 27 | assert.equal(process.getuid(), 0); 28 | assert.equal(posix.geteuid(), 2); 29 | 30 | posix.setreuid("root", "root"); 31 | assert.equal(posix.getuid(), 0); 32 | assert.equal(posix.geteuid(), 0); 33 | 34 | posix.setreuid(123, 456); 35 | assert.equal(posix.getuid(), 123); 36 | assert.equal(posix.geteuid(), 456); 37 | 38 | posix.setreuid(123, 456); // force Saved UID to be set, too (OSX) 39 | assert.throws(function() { 40 | posix.setreuid(0, 0); 41 | }, /EPERM/); 42 | 43 | assert.equal(posix.getuid(), 123); 44 | assert.equal(posix.geteuid(), 456); 45 | } 46 | 47 | if(process.getuid() == 0) { 48 | test_setreuid() 49 | } 50 | else { 51 | console.log("warning: setreuid tests skipped - not a privileged user!"); 52 | } 53 | -------------------------------------------------------------------------------- /test/unit/test-setrlimit.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var posix = require('../../lib/posix'); 3 | 4 | var limits = [ 5 | "core", 6 | "cpu", 7 | "data", 8 | "fsize", 9 | "nofile", 10 | "stack", 11 | "as" 12 | ]; 13 | 14 | var unsupportedLimits = []; 15 | 16 | if(['linux', 'darwin', 'freebsd'].indexOf(process.platform) !== -1) { 17 | limits.push('nproc'); 18 | } else { 19 | unsupportedLimits.push('nproc'); 20 | } 21 | 22 | // setrlimit: invalid input args 23 | try { 24 | posix.setrlimit(); 25 | assert.ok(false); 26 | } 27 | catch(e) { } 28 | 29 | try { 30 | posix.setrlimit("nofile"); 31 | assert.ok(false); 32 | } 33 | catch(e) { } 34 | 35 | try { 36 | posix.setrlimit("foobar", {soft: 100}); 37 | assert.ok(false); 38 | } 39 | catch(e) { } 40 | 41 | // verify that RLIM_INFINITY <-> null conversion works both ways 42 | posix.setrlimit("cpu", {soft: null, hard: null}); 43 | var now = posix.getrlimit("cpu"); 44 | assert.ok(now.soft === null); 45 | assert.ok(now.hard === null); 46 | 47 | // make some "nofile" adjustments 48 | var begin = posix.getrlimit("nofile") 49 | console.log("begin: " + JSON.stringify(begin)); 50 | posix.setrlimit("nofile", {soft: 500}); 51 | now = posix.getrlimit("nofile"); 52 | console.log("now: " + JSON.stringify(now)); 53 | assert.equal(begin.hard, now.hard); 54 | console.log("adjusted: " + JSON.stringify(now)); 55 | assert.equal(now.soft, 500); 56 | 57 | // setrlimit: lower each limit by one 58 | for(var i in limits) { 59 | var limit = posix.getrlimit(limits[i]); 60 | if(limit.soft > 1) { 61 | console.log("setrlimit: " + limits[i] + " " + JSON.stringify(limit)); 62 | posix.setrlimit(limits[i], { 63 | soft: limit.soft - 1, 64 | hard: limit.hard 65 | }); 66 | var limit2 = posix.getrlimit(limits[i]); 67 | assert.equal(limit.soft - 1, limit2.soft); 68 | console.log(limits[i] + " was: " + JSON.stringify(limit)); 69 | console.log(limits[i] + " now: " + JSON.stringify(limit2)); 70 | } 71 | } 72 | 73 | for(i in unsupportedLimits) { 74 | try { 75 | posix.setrlimit(unsupportedLimits[i], {soft: 100}); 76 | assert.ok(false); 77 | } 78 | catch(e) { } 79 | } 80 | -------------------------------------------------------------------------------- /test/unit/test-setsid.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var posix = require("../../lib/posix"); 3 | 4 | //////////////////////////////////////// 5 | // getpgid() 6 | assert.throws(function () { 7 | posix.getpgid(); 8 | }, /exactly one argument/); 9 | 10 | var my_pgid = posix.getpgid(0); 11 | assert.ok(my_pgid >= 0); 12 | assert.notEqual(my_pgid, process.pid); 13 | 14 | var parent_pgid = posix.getpgid(posix.getppid()); 15 | assert.equal(my_pgid, parent_pgid); 16 | 17 | assert.equal(posix.getpgid(1), 1); // init always has pgid==1 18 | 19 | //////////////////////////////////////// 20 | // setsid() 21 | assert.throws(function () { 22 | posix.setsid(123); 23 | }, /takes no arguments/); 24 | 25 | var sid = posix.setsid(); 26 | assert.equal(sid, process.pid); 27 | assert.equal(sid, posix.getpgid(0)); 28 | 29 | assert.throws(function () { 30 | posix.setsid(); 31 | }, /EPERM/); 32 | 33 | //////////////////////////////////////// 34 | // wrappers 35 | assert.equal(posix.getpgid(0), posix.getpgrp()); 36 | -------------------------------------------------------------------------------- /test/unit/test-syslog.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var posix = require("../../lib/posix"); 3 | 4 | assert.throws(function () { 5 | posix.openlog("foobar", 1); 6 | }, /invalid syslog constant value/); 7 | 8 | assert.throws(function () { 9 | posix.closelog("foobar"); 10 | }, /does not take any arg/); 11 | 12 | assert.throws(function () { 13 | posix.openlog("foobar", {"xxx": 1}, "local0"); 14 | }, /invalid syslog constant value/); 15 | 16 | assert.throws(function () { 17 | posix.openlog("foobar", {}, "xxx"); 18 | }, /invalid syslog constant value/); 19 | 20 | posix.openlog("test-node-syslog", {cons: true, ndelay: true, pid: true}, "local0"); 21 | posix.setlogmask({info:1, debug:1}); 22 | var old = posix.setlogmask({emerg:1, alert:1, crit:1, err:1, warning:1, 23 | notice:1, info:1, debug:1}); 24 | posix.syslog("info", "hello from node-posix (info)"); 25 | posix.closelog(); 26 | -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | import Options, Utils 2 | from os import unlink, symlink, popen 3 | from os.path import exists 4 | 5 | srcdir = '.' 6 | blddir = 'build' 7 | VERSION = '0.0.1' 8 | 9 | def set_options(opt): 10 | opt.tool_options('compiler_cxx') 11 | 12 | def configure(conf): 13 | conf.check_tool('compiler_cxx') 14 | conf.check_tool('node_addon') 15 | 16 | def build(bld): 17 | obj = bld.new_task_gen('cxx', 'shlib', 'node_addon') 18 | obj.cxxflags = ["-g", "-D_LARGEFILE_SOURCE", "-Wall"] 19 | obj.target = 'posix' 20 | obj.source = "./src/posix.cc" 21 | #obj.uselib = "PG" 22 | 23 | def test(test): 24 | print "TODO: implement" 25 | --------------------------------------------------------------------------------