Creating a simple Hello World tool that runs as a daemon - macos

I built the Command Line tool (Foundation) template in Xcode. It just logs "Hello World" to the console.There is only one class in it main.m. Here's the code:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
}
return 0;
}
Now I want to run it as a daemon and log "Hello World" to the console every 10 seconds. So I moved the product/binary to /tmp on my Mac. I created the following plist for launchd:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>helloDaemon</string>
<key>ProgramArguments</key>
<array>
<string>/tmp/helloDaemon</string>
</array>
<key>StartInterval</key>
<integer>10</integer>
</dict>
</plist>
I loaded the plist using launchctl, but I do not see any "Hello World"s in the console. Instead, I get this:
11/03/2012 00:55:35.141 com.apple.launchd: (helloDaemon) Throttling respawn: Will start in 1 seconds
11/03/2012 00:55:45.141 com.apple.launchd: (helloDaemon) Throttling respawn: Will start in 2 seconds
11/03/2012 00:55:55.140 com.apple.launchd: (helloDaemon) Throttling respawn: Will start in 3 seconds
So what's going wrong?

Just add to your launchd plist
<key>StandardOutPath</key>
<string>/yourpath/sample.log</string>
You can tail -f it afterwards.
More info is here:
http://developer.apple.com/library/mac/technotes/tn2083/_index.html#//apple_ref/doc/uid/DTS10003794-CH1-SUBSECTION39

NSLog is not working because when you launch a demon process it does not have any standard io sockets or file handles attached to it. They have to be specifically allocated. A good resource for how to create a proper daemon and how to write the console and syslog is provided in the book "Advanced Mac OS X Programming (chapter 20) by Dalrymple & Hillegass.
They have define a skeleton program that addresses the io issues you highlight. I remembered reading it a while ago and thought that maybe someday I'd need it. The authors show a sample using the syslog.h lib using openlog() and syslog() for simple communications. They also show some other lower level methods for communication to files and even sockets (for servers, etc).
I always appreciate when someone can tell me how to do something rather than reference something, but in this case, that is the best I can do. good luck.

With the following keys in the plist file I get most of the logs. But sometimes I am confused if I get all the NSLog output in the log file. To me it seems sometime some logs line are missing.
I am not sure if all the NSLog output of a LaunchDaemon is logged or some are skipped due to system priority.
<key>StandardOutPath</key>
<string>/var/log/mydaemon.log</string>
<key>StandardErrorPath</key>
<string>/var/log/mydaemon.log</string>

Related

Xcode - Archives not in organizer

Today I was looking to make an archive in Xcode 12 and suddenly I can no longer see the Archives in Window -> Organizer (it will not open automatically as supposed). It will show me the alert with Build Succeeded but no archive to upload on Test Flight. I was looking in ~Library/Developer/Xcode/Archives and is there but I can't see it in Organizer.
Any reason and maybe a solution for this?
Make sure you have the correct app selected. For example, this app I have no archives shown:
But once I select the correct app, it works:
I have managed to find a solution for this. So I was using react native mapbox package which seem to have some pods issues as per this thread https://github.com/react-native-mapbox-gl/maps/issues/1097#issuecomment-770064084
Looks like the archive was malformed
Try the follow:
1 - Close XCode.
2 - Go to /Library/Developer/XCode/Archives.
3 - Find the .xarchive file that you just archived.
4 - Delete the folders that are not the targets that you just archived (if exist):
Products/Applications/your-app-dev.app
Products/Applications/your-app-stage.app
5 - Open the Info.plist file (still from the .xarchive file).
6 - Complete the file with the part that is missing:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ApplicationProperties</key>
<dict>
<key>ApplicationPath</key>
<string>Applications/your-app.app</string>
<key>Architectures</key>
<array>
<string>arm64</string>
</array>
<key>CFBundleIdentifier</key>
<string>com.your-app.app</string>
<key>CFBundleShortVersionString</key>
<string>2.2</string>
<key>CFBundleVersion</key>
<string>42</string>
<key>SigningIdentity</key>
<string>Apple Development: Your Name (SOMEKEY123)</string>
<key>Team</key>
<string>123456789</string>
</dict>
<key>ArchiveVersion</key>
<integer>2</integer>
<key>CreationDate</key>
<date>2021-05-16T00:28:17Z</date>
<key>Name</key>
<string>your-app</string>
<key>SchemeName</key>
<string>your-app</string>
</dict>
</plist>

MacOS Metal: Failing to capture GPU Frame from command line app

I'm trying to programmatically capture GPU frames using MTLCaptureManager in a command line application.
So far, the capture manager fails to support the MTLCaptureDestinationGPUTraceDocument destination.
I tried to create a very minimal repro case using XCode :
#import <Foundation/Foundation.h>
#import <Metal/Metal.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
MTLCaptureManager* captureManager = [MTLCaptureManager sharedCaptureManager];
if (![captureManager supportsDestination:MTLCaptureDestinationGPUTraceDocument])
{
NSLog(#"********** captureManager does not support MTLCaptureDestinationGPUTraceDocument ************");
}
else
{
NSLog(#"captureManager support is fine");
}
}
return 0;
}
When run with XCode, it seems to be willing to work : the output is :
2020-09-02 16:25:59.712217+0200 testMetalCapture[20095:416447] Metal GPU Frame Capture Enabled
2020-09-02 16:25:59.712503+0200 testMetalCapture[20095:416447] Metal API Validation Enabled
2020-09-02 16:26:00.669092+0200 testMetalCapture[20095:416447] captureManager support is fine
Program ended with exit code: 0
But when I archive the build result, and run from a terminal, it fails :
2020-09-02 16:32:57.607 testMetalCapture[20126:419837] ********** captureManager does not support MTLCaptureDestinationGPUTraceDocument ************
Is there any runtime environment I could reproduce in terminal to get the MTLCaptureManager working ?
(Environment is XCode 11.6 + MacOS 10.15 Catalina)
From what I understood (I could not find any official documentation) :
MTLCaptureManager needs an authorization from the Info.plist: the MetalCaptureEnabled should set to YES.
The proper way is to bundle the command line application with such a plist
This is quite involved, see for example Building OSX App Bundle
I found by accident that MTLCaptureManager also works if there's an Info.plist in the same directory as the command line application.
This Info.plist can be almost empty, like this :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MetalCaptureEnabled</key>
<true/>
</dict>
</plist>
With this simple setup, I can run my test program (and my real one too)
% ./testMetalCapture
2020-10-02 15:53:08.507 testMetalCapture[28559:686864] Metal GPU Frame Capture Enabled
2020-10-02 15:53:08.523 testMetalCapture[28559:686864] captureManager support is fine
(I'm currently using MacOSX 10.15.6, it may break in the future)
The trick from #rotoglup was working for me, until it wasn't anymore: some buffers were full of zeros, and no image appeared in the 'screenshots' inside the metal debugger. I don't know if this was because of an update of the OS or of XCode or something else.
I still needed an Info.plist with this content, as #rotoglup suggested:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MetalCaptureEnabled</key>
<true/>
</dict>
</plist>
but I also needed to set the METAL_DEVICE_WRAPPER_TYPE env variable to 1 in the terminal that launches the command line app:
export METAL_DEVICE_WRAPPER_TYPE=1
With this, buffers are filled up correctly, and I get non-empty screenshots.

How to simulate keyboard and mouse events using CGEventPost in login window mac OS?

I have created a pre-login agent which uses CGEventPost for simulating keyboard. FYI I am developing a remote control app similar to teamviewer.
Keyboard
CGEventRef keyEvent = CGEventCreateKeyboardEvent( NULL, keyCode, down ) ;
CGEventPost( kCGHIDEventTap, keyEvent ) ;
CFRelease( keyEvent ) ;
Mouse
CGEventRef event = CGEventCreateMouseEvent(eventSource, eventType, mouseLocation, mouseButton );
CGEventPost(kCGHIDEventTap, event);
CFRelease(event);
Pre-login launch agent
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>my app label</string>
<key>LimitLoadToSessionType</key>
<string>LoginWindow</string>
<key>RunAtLoad</key>
<true/>
<key>WorkingDirectory</key>
<string>My app directory</string>
<key>ProgramArguments</key>
<array>
<string>app absolute path</string>
<string>service</string>
<string>myservice</string>
</array>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
CGEventPost is not working, I get the following in the Console logs after login
Untrusted apps are not allowed to connect to
Window Server before login.
I have searched o chromium's remote control (which has keyboard and mouse simulation working) source code. They use CGEventPost for keyboard, but it works in login window.
https://cs.chromium.org/chromium/src/remoting/host/input_injector_mac.cc?rcl=0&l=42
They seem to use a sh file in privileged helper tools directory and use to to load the service, I tried putting our service in privileged helpers tool, but still the event handling fails.
The Deprecated API CGPostMouseEvent, CGPostKeyBoardEvent work without problem , but would really like to know how non deprecated keyboard API works in chromium.
There is undocumented (classic apple , security through obscurity) stuff you should add to binary's sections, in order to make CGEventPost magically work when running in LoginWindow Context.
If you are using gcc, add the following to the compile flags
gcc <YOUR SOURCES AND FLAGS> -sectcreate CGPreLoginApp __CGPreLoginApp /dev/null
If you are using XCode, add the following to Other Linker flags in the Build settings of the project:
"-sectcreate"
__CGPreLoginApp
__cgpreloginapp
/dev/null
Refer https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDFamily.xcodeproj/project.pbxproj project file for the LDFLAGS

Updating the Perm Gen Memory Jenkins - MacOSX

I am trying to update the Perm Gen Memory in Jenkins, from what I have read adding this to the org.jenkins.plist file would do the trick but it is not changing it for me:
<key>-XX:PermSize</key>
<string>512m</string>
<key>-XX:MaxPermSize</key>
<string>1024m</string>
When I use the Jenkins monitoring tool is still tells me that:
Perm Gen Memory: 81mb
Am I doing something wrong?
Thanks
According to http://mgrebenets.github.io/mobile%20ci/2015/02/01/jenkins-ci-server-on-osx, you should be using <string> but not <key>, eg:
<string>-XX:MaxPermSize=1024m</string>
<key> denotes a section. In this case, you are setting program arguments that fall under the <key>ProgramArguments</key> section. What you specified as key sections is probably confusing the launcher. Look at that link for a complete example and compare to yours.
Abridged example:
<plist version="1.0">
<dict>
<key>Label</key>
<string>homebrew.mxcl.jenkins</string>
<key>ProgramArguments</key>
<array>
..... more props here...
<string>-XX:MaxPermSize=256m</string>
.... more props here
</array>
... more stuff here...
</dict>
Also note the instructions for properly restarting (unloading/loading) the instance so that the changes are applied. Once you restart it, confirm that the options you set translated properly to the command line:
ps aux | grep java
The properties should be properly formatted as follows:
-XX:PermSize=512m -XX:MaxPermSize=1024m

launchd.plist runs every 10 seconds instead of just once

I have been setting up a launchd.plist XML that is run every time a specific USB device is mounted. I followed the instructions on the xpc_events(3) man page and it is running the application whenever the device is mounted.
The problem I'm having is that the application is run again and again every 10 seconds as long as the device is still mounted. How can I set it up so it only runs once when the device is inserted in the USB port?
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.myapp.agent</string>
<key>Program</key>
<string>/Applications/MyApp.app/Contents/MacOS/MyAgent</string>
<key>LaunchEvents</key>
<dict>
<key>com.apple.iokit.matching</key>
<dict>
<key>com.apple.device-attach</key>
<dict>
<key>idVendor</key>
<integer>2316</integer>
<key>idProduct</key>
<integer>4096</integer>
<key>IOProviderClass</key>
<string>IOUSBDevice</string>
<key>IOMatchLaunchStream</key>
<true/>
</dict>
</dict>
<key>com.apple.notifyd.matching</key>
<dict>
<key>com.apple.interesting-notification</key>
<dict>
<key>Notification</key>
<string>com.apple.interesting-notification</string>
</dict>
</dict>
</dict>
</dict>
</plist>
I wrote a tutorial on this with detailed instructions and example files for triggering an arbitrary executable or shell script by the connection of an external device (usb/thunderbolt) to a Mac computer, without the respawning problem.
Like the authors approach, it relies on Apple's IOKit library for device detection and a daemon for running the desired executable. For the daemon to not be triggered repeatedly after connecting the device, a special stream handler (xpc_set_event_stream_handler) is used to "consume" the com.apple.iokit.matching event, as explained in the post by #ford and in his github repo.
In particular, the tutorial describes how to compile the xpc stream handler and how to reference it together with the executable in the daemon plist file and where to place all the relevant files with correct permissions.
For the files, please go here. For completeness, I have also pasted their content below.
Run shell script or executable triggered by device detection on a mac
Here I use the example of spoofing the MAC address of an ethernet adapter when it is connected to the Mac. This can be generalized to arbitrary executables and devices.
Put your shell script or executable into place
Adapt the shell script spoof-mac.sh
#!/bin/bash
ifconfig en12 ether 12:34:56:78:9A:BC
to your needs and
make it executable:
sudo chmod 755 spoof-mac.sh
Then move it into /usr/local/bin, or some other directory:
cp spoof-mac.sh /usr/local/bin/
Building the stream handler
The stream handler xpc_set_event_stream_handler.m
// Created by Ford Parsons on 10/23/17.
// Copyright © 2017 Ford Parsons. All rights reserved.
//
#import <Foundation/Foundation.h>
#include <xpc/xpc.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
xpc_set_event_stream_handler("com.apple.iokit.matching", NULL, ^(xpc_object_t _Nonnull object) {
const char *event = xpc_dictionary_get_string(object, XPC_EVENT_KEY_NAME);
NSLog(#"%s", event);
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if(argc >= 2) {
execv(argv[1], (char **)argv+1);
}
}
}
is universal (no need to adapt) and can be built on a mac command line (with xcode installed):
gcc -framework Foundation -o xpc_set_event_stream_handler xpc_set_event_stream_handler.m
Let's place it into /usr/local/bin, like the main executable for the daemon.
cp xpc_set_event_stream_handler /usr/local/bin/
Setup the daemon
The plist file com.spoofmac.plist contains the properties of the daemon that will run the executable on device connect trigger.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UserName</key>
<string>root</string>
<key>StandardErrorPath</key>
<string>/tmp/spoofmac.stderr</string>
<key>StandardOutPath</key>
<string>/tmp/spoofmac.stdout</string>
<key>Label</key>
<string>com.spoofmac.program</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/xpc_set_event_stream_handler</string>
<string>/usr/local/bin/spoofmac.sh</string>
</array>
<key>LaunchEvents</key>
<dict>
<key>com.apple.iokit.matching</key>
<dict>
<key>com.apple.device-attach</key>
<dict>
<key>idVendor</key>
<integer>32902</integer>
<key>idProduct</key>
<integer>5427</integer>
<key>IOProviderClass</key>
<string>IOPCIDevice</string>
<key>IOMatchLaunchStream</key>
<true/>
<key>IOMatchStream</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>
It contains information for identifying the device you want to base your trigger on, like idVendor, idProduct, IOProviderClass. These can be figured out in the System Information App on your mac.
Convert the hex identifiers to integers before inserting into the plist file (for example using int(0x8086) in python).
IOProviderClass should be either IOPCIDevice (Thunderbolt) or IOUSBDevice (USB).
The other relevant entry in the plist file is the location of xpc_set_event_stream_handler and the executable.
Other entries include the location of standard output (log) files and the executing user.
Since MAC spoofing requires root privileges, we put com.spoofmac.plist into /Library/LaunchDaemons:
cp com.spoofmac.plist /Library/LaunchDaemons/
not into a LaunchAgents folder. Launch agents ignore the UserName argument.
Insure that the owner of the file is root:
sudo chown root:wheel /Library/LaunchDaemons/com.spoofmac.plist
Launch the daemon
Activate the daemon:
launchctl load /Library/LaunchDaemons/com.spoofmac.plist
and you are good to go.
Unloading is done using launchctl unload.
AIUI your application must call xpc_set_event_stream_handler to remove the event from the queue. You might also have to add <key>KeepAlive</key><false/> to the .plist, but I'm not sure about that.
I am trying to use something like this:
#include <xpc/xpc.h>
#include <unistd.h>
#include <asl.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
return 1;
}
asl_log(NULL, NULL, ASL_LEVEL_DEBUG, "event_stream_handler: starting");
xpc_set_event_stream_handler("com.apple.iokit.matching", dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(xpc_object_t event) {
const char *name = xpc_dictionary_get_string(event, XPC_EVENT_KEY_NAME);
uint64_t id = xpc_dictionary_get_uint64(event, "IOMatchLaunchServiceID");
asl_log(NULL, NULL, ASL_LEVEL_DEBUG, "event_stream_handler: received event: %s: %llu", name, id);
execv(argv[1], argv + 1);
});
dispatch_main();
return 0;
}
So a script which consumes the event and runs the script passed as an argument.
This works for me:
int main(int argc, const char * argv[]) {
#autoreleasepool {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
xpc_set_event_stream_handler("com.apple.iokit.matching", NULL, ^(xpc_object_t _Nonnull object) {
const char *event = xpc_dictionary_get_string(object, XPC_EVENT_KEY_NAME);
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if(argc >= 2) {
execv(argv[1], (char **)argv+1);
}
}
}
full source code here

Resources