I'm trying to change my process' name as it appears in ps
and Activity Monitor
at runtime. I found several notes that there is no portable way to do this (which I don't care about).
Here's what I tried. None of these approaches worked for me.
argv[0]
(seems to be the way to go on some Unix systems)[[NSProcessInfo processInfo] setProcessName:@"someName"]
setprogname
(calling getprogname
returns the name I set, but that is irrelevant)I also read about a function called setproctitle
which should be defined in stdlib.h
if it is available, but it's not there.
There must be a way to accomplish this because QTKitServer - the faceless decoder for QuickTime Player X - has its corresponding QuickTime Player's PID in its process name.
Does anybody have a clue about how to accomplish this? I'd very much prefer a Core Foundation or POSIXy way over an Objective-C method to do this.
Thanks,
Marco
Edit: If it is in any way relevant, I'm using Mac OS X 10.6.5 and Xcode 3.2.5
There are good reasons to change the process name. Java software should change process names because when running different java tools I want to see which java process is for which tool.
Chromium does it: http://src.chromium.org/viewvc/chrome/trunk/src/base/mac/mac_util.mm.
Node.js uses same code to implement Process.title = 'newtitle'
: https://github.com/joyent/node/blob/master/src/platform_darwin_proctitle.cc
Note: This fails if someone does su
to a different not logged-in user: https://github.com/joyent/node/issues/1727
Here the source code in its full complex glory. By the way, someone told me it also works for Mac OS X Lion and also fails with su
.
// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. void SetProcessName(CFStringRef process_name) { if (!process_name || CFStringGetLength(process_name) == 0) { NOTREACHED() << "SetProcessName given bad name."; return; } if (![NSThread isMainThread]) { NOTREACHED() << "Should only set process name from main thread."; return; } // 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; static bool did_symbol_lookup = false; if (!did_symbol_lookup) { did_symbol_lookup = true; CFBundleRef launch_services_bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices")); if (!launch_services_bundle) { LOG(ERROR) << "Failed to look up LaunchServices bundle"; return; } ls_get_current_application_asn_func = reinterpret_cast<LSGetCurrentApplicationASNType>( CFBundleGetFunctionPointerForName( launch_services_bundle, CFSTR("_LSGetCurrentApplicationASN"))); if (!ls_get_current_application_asn_func) LOG(ERROR) << "Could not find _LSGetCurrentApplicationASN"; ls_set_application_information_item_func = reinterpret_cast<LSSetApplicationInformationItemType>( CFBundleGetFunctionPointerForName( launch_services_bundle, CFSTR("_LSSetApplicationInformationItem"))); if (!ls_set_application_information_item_func) LOG(ERROR) << "Could not find _LSSetApplicationInformationItem"; CFStringRef* key_pointer = reinterpret_cast<CFStringRef*>( CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSDisplayNameKey"))); ls_display_name_key = key_pointer ? *key_pointer : NULL; if (!ls_display_name_key) LOG(ERROR) << "Could not find _kLSDisplayNameKey"; // 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); } if (!ls_get_current_application_asn_func || !ls_set_application_information_item_func || !ls_display_name_key) { return; } PrivateLSASN asn = ls_get_current_application_asn_func(); // Constant used by WebKit; what exactly it means is unknown. const int magic_session_constant = -2; OSErr err = ls_set_application_information_item_func(magic_session_constant, asn, ls_display_name_key, process_name, NULL /* optional out param */); LOG_IF(ERROR, err) << "Call to set process name failed, err " << err; }
Edit: It's a complex and confusing problem.
On OS X there is no setproctitle(3). One has to write into the argv array (ugly and a bit dangerous because it is possible to overwrite some environment variables with bogus stuff). Done correctly it works very well.
Additionally Apple has the ActivityMonitor application, something like the Task Manager under Windows. The code above manipulates ActivityMonitor but this manipulation doesn't seem to be encouraged by Apple (hence the use of undocumented functions).
Important: ps and ActivityMonitor don't show the same information.
Also important: ActivityMonitor is not available if you don't have GUI. This can happen if you ssh in to a remote Apple box and there is nobody logged in by GUI. Sadly there is a bug by Apple IMO. Just querying if there is a GUI sends an annoying warning message to stderr.
Summary: If you need to change ActivityMonitor, use the code above. If you have GUI-less situations and and dislike warnings on stderr, redirect stderr temporarily to /dev/null during the call of SetProcessName
. If you need to change ps information, write into argv.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With