$ node
> var m = require('./uid-process');
undefined
> m.uid('root', function(err,uid) {
... if (err) return console.error("got error:", err);
... console.log("uid:", uid);
... });
undefined
> uid: 0
...any problems with this?
What happens if you run this 1000 times?
$ cat manytimes.js
var m = require('./uid-process');
var counter = process.argv[2] || 1000;
console.log("getting uid", counter, "times");
for (var i = 0; i < counter; i++) {
m.uid('root', function(err,uid) {
if (err) return console.error("got error:", err);
});
};
$ node manytimes.js
getting uid times: 1000
child_process.js:927
throw errnoException(process._errno, 'spawn');
^
Error: spawn EAGAIN
at errnoException (child_process.js:980:11)
at ChildProcess.spawn (child_process.js:927:11)
...
:(
a better way
$ man getpwnam
NAME
getpwent, getpwnam, getpwnam_r, getpwuid, getpwuid_r, getpwuuid, getpwuuid_r, setpassent, setpwent, endpwent -- password database operations
LIBRARY
Standard C Library (libc, -lc)
SYNOPSIS
#include <sys/types.h>
#include <pwd.h>
#include <uuid/uuid.h>
struct passwd * getpwnam(const char *login);
"The getpwnam() function returns a pointer to a structure containing the broken-out fields of the record in the password database (e.g., the local password file /etc/passwd, NIS, and LDAP) that matches the username name."
struct passwd
The passwd structure is defined in as follows:
struct passwd {
char *pw_name; /* username */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */ <- we want this!
gid_t pw_gid; /* group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
Goal
let's build a binding for getpwname :)
/**
* Get the uid for username.
*/
var pwname = require('./pwname');
var username = process.argv[2] || 'root';
try {
console.log("Uid for", username, "is", pwname.uid(username));
} catch (err) {
console.error("User does not exist:", username, err);
}
Getting Started
You'll need
node-gyp
$ npm install node-gyp -g
gcc and other build tools for your environment
if you can build node.js, you are probably good to go: https://github.com/joyent/node/wiki/Installation
basic module structure
pwname/
binding.gypnode-gyp build script
lib/
pwname.jsjs module entry point (not strictly required)
var path = require('path');
var _pwname = require(path.join(__dirname, '../build/Release/pwname.node'));
/**
* Get uid for user.
*
* @param {String} username - username
*
* @return {Integer} user id of user
*
*/
exports.uid = function(username) {
return _pwname.uid(username);
};
Our built node module in build/Release
src/pwname.cc
the c++ code
Now for the good stuff
src/pwname.cc
the basic structure
#include <v8.h>
#include <node.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
using namespace node;
using namespace v8;
Handle<Value> Uid(const Arguments& args);
Local<T> and Handle<T> contents freed when out of scope
void UselessFunction() {
HandleScope scope;
Local<Value> helloHandle = String::New("hello"); // allocation here
} // scope ends. The string pointed to by helloHandle is auto-deleted.
Persistent<T> handles must be manually disposed.
Handle<Value> UselessFunction() {
HandleScope scope;
Persistent<Value> helloHandle = Persistent<String>::New(String::New("hello")); // allocation here
return helloHandle;
} // v8::String will live until handle.Dispose() is called
var Time = require('./simpletime').Time;
var time = new Time();
console.log("Epoch is", time.epoch()); // Epoch is 1371486877
console.log("Time is", time.string()); // Time is Mon Jun 17 12:34:37 2013
time.addSeconds(10);
console.log("Epoch is", time.epoch()); // Epoch is 1371486887
console.log("Time is", time.string()); // Time is Mon Jun 17 12:34:47 2013
Our tools
#include <stdio.h>
#include <time.h>
int main ()
{
time_t rawtime;
struct tm * timeinfo;
char timestring[26];
time (&rawtime); /* get the current time and store in rawtime */
timeinfo = localtime (&rawtime); /* convert time -> timeinfo */
strftime(timestring, 26, "%c", timeinfo);
printf ("Current local time and date: %s\n", timestring);
return 0;
}
"Here's a fun fact: every function call that does CPU work also blocks. This function, which calculates the n'th Fibonacci number, will block the current thread of execution because it's using the CPU."
-- `node js is cancer` by Ted Dzuiba
function fibonacci(n) {
if (n <= 0)
return 0;
if (n == 1)
return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
Yes, it's possible
var fibonacci = require('build/Release/fibonacci.node');
var n = 20;
fibonacci.calculate(n, function(err, result) {
console.log("Fibonacci for n", n, "is", result);
});
console.log("Hey! This executes right away, cuz fibonacci is async! ...");
Runs FibonacciWorker function in a thread from libuv's worker pool
On completion, FibonacciWorkerAfter executed in event loop.
context->request is an uv_work_tobject that stores our input/output values, and gets supplied to both functions.
Context for uv_queue_work worker functions
struct FibonacciContext {
uv_work_t request;int input; // the value passed to fibonacci();unsignedint result; // save the result to be calculated
Persistent<Function> callback; // js callback
FibonacciContext(int n, Handle<Function> callback) {
this->input = n;
this->callback = Persistent<Function>::New(callback);
// set the data to point to myselfthis->request.data = this;
}
~FibonacciContext() {
this->request.data = NULL;
this->callback.Dispose();
}
};
request->data == FibonacciContext
fibonacci.calculate()
Handle<Value> Calculate(const Arguments& args) {
HandleScope scope;
Local<Function> callback;
int n;
if (args.Length() > 0 && args[0]->IsNumber()) {
n = args[0]->ToInteger()->Value();
} else {
return ThrowException(Exception::Error(
String::New("You must supply an integer")));
}
if (args.Length() > 1 && args[1]->IsFunction()) {
callback = Local<Function>::Cast(args[1]);
} else {
return ThrowException(Exception::Error(
String::New("You must supply a callback")));
}
FibonacciContext *context =
new FibonacciContext(n, callback);
uv_queue_work(uv_default_loop(), &context->request,
FibonacciWorker, FibonacciWorkerAfter);
return scope.Close(Undefined());
}