diff --git a/LICENSE b/LICENSE index 7b72de07190..108cdf58dc8 100644 --- a/LICENSE +++ b/LICENSE @@ -33,6 +33,9 @@ are: and has copyrights from Jérémy Lal, Ryan Tomayko, Dominic Baggott, Ash Berlin, and Joey Mazzarelli. + - src/platform_darwin_proctitle.cc, has code taken from the Chromium + project copyright Google Inc. and released with the BSD license. + Node's license follows: diff --git a/src/platform_darwin.cc b/src/platform_darwin.cc index 16897b28d97..f02dc494a39 100644 --- a/src/platform_darwin.cc +++ b/src/platform_darwin.cc @@ -7,19 +7,25 @@ #include /* PATH_MAX */ namespace node { - +static char *process_title; char** OS::SetupArgs(int argc, char *argv[]) { + process_title = argc ? strdup(argv[0]) : NULL; return argv; } -void OS::SetProcessTitle(char *title) { - ; -} +// OS::SetProcessTitle implemented in platform_darwin_proctitle.cc +} // namespace node +#include "platform_darwin_proctitle.cc" +namespace node { const char* OS::GetProcessTitle(int *len) { + if (process_title) { + *len = strlen(process_title); + return process_title; + } *len = 0; return NULL; } diff --git a/src/platform_darwin_proctitle.cc b/src/platform_darwin_proctitle.cc new file mode 100644 index 00000000000..6c58ae1c948 --- /dev/null +++ b/src/platform_darwin_proctitle.cc @@ -0,0 +1,146 @@ +// Copyright 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This code emanates from the Chromium project: +// http://src.chromium.org/viewvc/chrome/trunk/src/base/mac_util.mm + +#include +#include // Oh noes! See discussion blow at GetCurrentProcess +#ifndef NDEBUG + #include +#endif + +namespace node { + +void OS::SetProcessTitle(char *title) { + static int symbol_lookup_status = 0; // 1=ok, 2=unavailable + if (symbol_lookup_status == 2) { + // feature is unavailable + return; + } + + if (process_title) free(process_title); + process_title = strdup(title); + + // Warning: here be dragons! This is SPI reverse-engineered from WebKit's + // plugin host, and could break at any time (although realistically it's only + // likely to break in a new major release). + // When 10.7 is available, check that this still works, and update this + // comment for 10.8. + + // Private CFType used in these LaunchServices calls. + typedef CFTypeRef PrivateLSASN; + typedef PrivateLSASN (*LSGetCurrentApplicationASNType)(); + typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLSASN, + CFStringRef, + CFStringRef, + CFDictionaryRef*); + + static LSGetCurrentApplicationASNType ls_get_current_application_asn_func = + NULL; + static LSSetApplicationInformationItemType + ls_set_application_information_item_func = NULL; + static CFStringRef ls_display_name_key = NULL; + if (!symbol_lookup_status) { + CFBundleRef launch_services_bundle = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices")); + if (!launch_services_bundle) { +#ifndef NDEBUG + warnx("failed to look up LaunchServices bundle"); +#endif + symbol_lookup_status = 2; + return; + } + + ls_get_current_application_asn_func = + reinterpret_cast( + CFBundleGetFunctionPointerForName( + launch_services_bundle, CFSTR("_LSGetCurrentApplicationASN"))); + if (!ls_get_current_application_asn_func) { +#ifndef NDEBUG + warnx("could not find _LSGetCurrentApplicationASN"); +#endif + symbol_lookup_status = 2; + return; + } + + ls_set_application_information_item_func = + reinterpret_cast( + CFBundleGetFunctionPointerForName( + launch_services_bundle, + CFSTR("_LSSetApplicationInformationItem"))); + if (!ls_set_application_information_item_func) { +#ifndef NDEBUG + warnx("Could not find _LSSetApplicationInformationItem"); +#endif + symbol_lookup_status = 2; + return; + } + + const CFStringRef* key_pointer = reinterpret_cast( + CFBundleGetDataPointerForName(launch_services_bundle, + CFSTR("_kLSDisplayNameKey"))); + ls_display_name_key = key_pointer ? *key_pointer : NULL; + if (!ls_display_name_key) { +#ifndef NDEBUG + warnx("Could not find _kLSDisplayNameKey"); +#endif + symbol_lookup_status = 2; + return; + } + + // Internally, this call relies on the Mach ports that are started up by the + // Carbon Process Manager. In debug builds this usually happens due to how + // the logging layers are started up; but in release, it isn't started in as + // much of a defined order. So if the symbols had to be loaded, go ahead + // and force a call to make sure the manager has been initialized and hence + // the ports are opened. + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + symbol_lookup_status = 1; // 1=ok + } + + PrivateLSASN asn = ls_get_current_application_asn_func(); + // Constant used by WebKit; what exactly it means is unknown. + const int magic_session_constant = -2; + CFStringRef process_name = + CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); + OSErr err = + ls_set_application_information_item_func(magic_session_constant, asn, + ls_display_name_key, + process_name, + NULL /* optional out param */); +#ifndef NDEBUG + if (err) { + warnx("Call LSSetApplicationInformationItem failed"); + } +#endif +} + +} // namespace node diff --git a/test/disabled/test-process-title.js b/test/disabled/test-process-title.js new file mode 100644 index 00000000000..d0223d9a1cf --- /dev/null +++ b/test/disabled/test-process-title.js @@ -0,0 +1,39 @@ +var common = require("../common"); +var assert = common.assert; +var spawn = require('child_process').spawn; + +if (process.title === '') { + console.log('skipping test -- not implemented for the host platform'); + return; +} + +// disabled because of two things +// - not tested on linux (can ps show process title on Linux?) +// - unable to verify effect on Darwin/OS X (only avail through GUI tools AFAIK) + +function verifyProcessName(str, callback) { + process.title = str; + var buf = ''; + ps = spawn('ps'); + ps.stdout.setEncoding('utf8'); + ps.stdout.addListener("data", function (s) { buf += s; }); + ps.addListener("exit", function (c) { + try { + assert.equal(0, c); + assert.ok(new RegExp(process.pid+' ', 'm').test(buf)); + assert.ok(new RegExp(str, 'm').test(buf)); + callback(); + } catch (err) { + callback(err); + } + }); +} + +verifyProcessName("3kd023mslkfp--unique-string--sksdf", function(err){ + if (err) throw err; + console.log('title is now %j', process.title); + verifyProcessName("3kd023mslxxx--unique-string--xxx", function(err){ + if (err) throw err; + console.log('title is now %j', process.title); + }); +}); diff --git a/wscript b/wscript index f55ea5d88b5..ba6051471f0 100644 --- a/wscript +++ b/wscript @@ -252,6 +252,9 @@ def configure(conf): conf.env.append_value ('CCFLAGS', threadflags) conf.env.append_value ('CXXFLAGS', threadflags) conf.env.append_value ('LINKFLAGS', threadflags) + if sys.platform.startswith("darwin"): + # used by platform_darwin_*.cc + conf.env.append_value('LINKFLAGS', ['-framework','Carbon']) conf.env.append_value("CCFLAGS", "-DX_STACKSIZE=%d" % (1024*64))