#include <stdio.h>
#include <stddef.h>
#include <libc.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/disk.h>
#include <errno.h>
#include <sys/param.h>
#include <unistd.h>
#include <fts.h>
#include <sys/syslimits.h>
#include <mach/mach_error.h>
#include "DiskArbitrationPrivate.h"
#include <CoreFoundation/CoreFoundation.h>
int refresh = 0;
int do_unmount = 0;
int punmount = 0;
int eject = 0;
int afpmount = 0;
int afpdismount = 0;
int do_mount = 0;
int newname = 0;
int refuse = 0;
int refuseMounts = 0;
int list = 0;
char *device;
char *mountpoint;
mach_port_t diskArbitrationPort;
mach_msg_header_t * msgSendBufPtr;
void
MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info )
{
unsigned long result = 0xFFFFFFFF;
mach_msg_header_t * receivedMessage;
receivedMessage = ( mach_msg_header_t * ) msg;
result = DiskArbHandleMsg ( receivedMessage, msgSendBufPtr );
( void ) mach_msg_send ( msgSendBufPtr );
}
void
DisplayHelp()
{
printf("disktool: Disk Arbitration Command Tool\n");
printf("disktool: disktool -[r][a][u][e][m][p][n][d][s][g][A][S][D][l]\n");
printf(" deviceName [options]\n");
printf("You can use disktool to rename, eject, mount or unmount disks and volumes\n");
printf("The acceptable command line parameters are:\n");
printf("\nInformation about disks:\n");
printf("\tUsage: -l -- List Disks. Lists the disks currently known and available\n");
printf("\t on the system.\n");
printf("\nControlling arbitration:\n");
printf("\tUsage: -r -- Refresh Disk Arbitration. Causes arbitration to refresh\n");
printf("\t its internal tables and look for new mounts/unmounts.\n");
printf("\nManaging disks:\n");
printf("\tUsage: -u -- Unmount a disk (ex. disktool -u disk2)\n");
printf("\tUsage: -p -- Unmount a partition (ex. disktool -p disk1s2)\n");
printf("\tUsage: -e -- Eject a disk (ex. disktool -e disk2)\n");
printf("\tUsage: -m -- Mount a disk (ex. disktool -m disk2). Useful when a disk\n");
printf("\t has been unmounted by hand (using -p or -u parameters\n");
printf("\tUsage: -a -- Notify of mount. Adds the disk to the Disk Arbitrations\n");
printf("\t internal tables.\n");
printf("\t Useful when you have already forced the mount and want \n");
printf("\t to let applications know it.\n");
printf("\t (ex. disktool -a disk1 AFPVolName AFPFlags)\n");
printf("\tUsage: -d -- Notify of dismount. Removes the disk from Disk\n");
printf("\t Arbitration's internal tables.\n");
printf("\t Useful when you have already forced the unmount and \n");
printf("\t want to let applications know it.\n");
printf("\t (ex. disktool -d disk1)\n");
printf("\nControlling disk parameters:\n");
printf("\tUsage: -n -- Rename volume. Renames the volume specified as the first\n");
printf("\t argument. (ex. disktool -n disk1s2 newName)\n");
printf("\tUsage: -g -- Get the hfs encoding on a volume.\n");
printf("\t (ex. disktool -g disk1s2)\n");
printf("\tUsage: -s -- Set the hfs encoding on a volume.\n");
printf("\t (ex. disktool -s disk1s2 4)\n");
printf("\tUsage: -A -- Adopts the given device into the volinfo database.\n");
printf("\tUsage: -D -- Disowns the given device from the volinfo database.\n");
printf("\tUsage: -S -- Displays the status of the device in the volinfo database.\n");
fflush(stdout);
return;
}
void
DiskApproval_callback(DiskArbDiskIdentifier diskIdentifier,
DiskArbMountpoint possibleMountpoint,
DiskArbGenericString ioContent,
DiskArbGenericString deviceTreePath,
int diskType,
int isWritable,
int isRemovable,
int isWhole,
DiskArbGenericString fsType)
{
printf("Disallowing disk %s to mount with media name %s. Params: %s, %s, %s\n", diskIdentifier, possibleMountpoint, (isWritable ? "Writable" : "Read Only"), (isRemovable ? "Removable" : "Fixed"), (isWhole ? "Whole Disk" : "Partition"));
DiskArbDiskApprovedAck_auto(diskIdentifier, kDiskArbDisallowMounting );
}
void
unrecognizedCallback(DiskArbDiskIdentifier diskIdentifier, int diskType, char *fsType, char *deviceType, int isWritable, int isRemovable, int isWhole)
{
printf("Unrecognized disk %s appeared with type = %d\n", diskIdentifier, diskType);
sleep(1);
DiskArbClientWillHandleUnrecognizedDisk(diskIdentifier, 1);
}
void
CallFailed_callback(DiskArbDiskIdentifier diskIdentifier,
int type,
int error)
{
printf("*** Call Failed %s, %d, %d ***\n", diskIdentifier, type, error);
exit(0);
}
void
reserveCallback(
DiskArbDiskIdentifier diskIdentifier,
unsigned status,
unsigned pid)
{
printf("Woohoo %d\n", status);
fflush(stdout);
if (status == kDiskArbDeviceReservationRefused) {
printf("Darn it %d refused me\n", pid);
fflush(stdout);
exit(0);
}
if (status == kDiskArbDeviceIsReserved) {
printf("Darn it %s is reserved by %d\n", diskIdentifier, pid);
fflush(stdout);
exit(0);
}
if (status == kDiskArbDeviceIsNotReserved) {
printf("it's free\n");
fflush(stdout);
exit(0);
}
if (status == kDiskArbDeviceReservationObtained) {
printf("Yeah - I've got it - now I hold it\n");
fflush(stdout);
}
}
void
willRelease(DiskArbDiskIdentifier diskIdentifier, int releaseToClientPid)
{
printf("Someone (%d) has asked me to give up my reserve on %s\n", releaseToClientPid, diskIdentifier);
fflush(stdout);
DiskArbClientRelinquishesReservation(diskIdentifier, releaseToClientPid, 1);
}
void
DiskAppearedComplete_callback(
DiskArbDiskIdentifier diskIdentifier,
unsigned flags,
DiskArbMountpoint mountpoint,
DiskArbIOContent ioContent,
DiskArbDeviceTreePath deviceTreePath,
unsigned sequenceNumber,
double timeAppeared,
DiskArbGenericString fsType,
DiskArbGenericString fsName)
{
printf("***Disk Appeared ('%s',Mountpoint = '%s', fsType = '%s', volName = '%s')\n", (char *) diskIdentifier, (char *) mountpoint, (char *)fsType, (char *)fsName);
fflush(stdout);
if (do_mount && !strcmp(device, diskIdentifier)) {
printf("Disk Mounting Completed\n");
fflush(stdout);
exit(0);
}
return;
}
void
UnmountPreNotification_callback(DiskArbDiskIdentifier diskIdentifier, unsigned flags)
{
kern_return_t err;
err = DiskArbUnmountPreNotifyAck_async_auto(diskIdentifier, refuse);
printf("***Responding %s to unmount - %s\n", (refuse ? "no" : "yes"), diskIdentifier);
fflush(stdout);
return;
}
void
DiskApprovedAck_callback(DiskArbDiskIdentifier diskIdentifier, unsigned flags)
{
kern_return_t err;
err = DiskArbDiskApprovedAck_auto(diskIdentifier, 1);
return;
}
void
UnmountPostNotification_callback(
DiskArbDiskIdentifier diskIdentifier,
int errorCode,
pid_t pid)
{
if (!errorCode) {
printf("***Disk Unmounted('%s')\n", (char *) diskIdentifier);
fflush(stdout);
} else {
printf("***Disk NOT Unmounted('%s'), errorCode(%d), dissenter(%d)\n", (char *) diskIdentifier, errorCode, (int) pid);
fflush(stdout);
}
if ((do_unmount || punmount) && !strcmp(device, diskIdentifier)) {
exit(0);
}
}
void
EjectPreNotification_callback(
DiskArbDiskIdentifier diskIdentifier,
unsigned flags)
{
kern_return_t err;
printf("***Responding %s to eject - %s\n", (refuse ? "no" : "yes"), diskIdentifier);
fflush(stdout);
err = DiskArbEjectPreNotifyAck_async_auto(diskIdentifier, refuse);
}
void
EjectPostNotification_callback(
DiskArbDiskIdentifier diskIdentifier,
int errorCode,
pid_t pid)
{
if (!errorCode) {
printf("***Disk Ejected('%s')\n", (char *) diskIdentifier);
fflush(stdout);
} else {
printf("***Disk NOT Ejected('%s'), errorCode(%d), dissenter(%d)\n", (char *) diskIdentifier, errorCode, (int) pid);
fflush(stdout);
}
if (eject && !strcmp(device, diskIdentifier)) {
exit(0);
}
}
void
DiskChanged_callback(DiskArbDiskIdentifier diskIdentifier,
char *mountpoint,
char *newVolumeName,
int flags,
int success)
{
printf("***DiskChanged('%s', '%s', '%s', %d, Success = %d)\n", diskIdentifier, mountpoint, newVolumeName, flags, success);
if (newname && !strcmp(device, diskIdentifier)) {
printf("Got end signal\n");
fflush(stdout);
exit(0);
}
}
void
NotificationComplete_callback(int messageType)
{
if (list == 1 && messageType == 1) {
exit(0);
}
printf("***Notifications Complete for type %d\n", messageType);
fflush(stdout);
}
int
testDevice(char *devname)
{
char deviceName[255];
struct stat sb;
if (strncmp(devname, "disk", strlen("disk"))) {
return 1;
}
sprintf(deviceName, "/dev/%s", devname);
if (stat(deviceName, &sb) == 0) {
return 1;
} else {
printf("Cannot find device %s\n", deviceName);
return 0;
}
}
int
main(int argc, const char *argv[])
{
kern_return_t err;
unsigned msgSendBufLength;
int flags = 0;
char ch;
int afpflags = 0;
int vsdb = 0;
int vsdbDisplay = 0;
int vsdbAdopt = 0;
int vsdbDisown = 0;
int getEncoding = 0;
int setEncoding = 0;
int unrecognizedMessages = 0;
int hideUnrecognizedMessages = 0;
int retain = 0, test = 0, release = 0;
CFMachPortRef cfMachPort = 0;
CFMachPortContext context;
CFRunLoopSourceRef cfRunLoopSource;
Boolean shouldFreeInfo;
context.version = 1;
context.info = 0;
context.retain = NULL;
context.release = NULL;
context.copyDescription = NULL;
if (argc < 2) {
DisplayHelp();
goto Exit;
}
while ((ch = getopt(argc, (char * const *)argv, "hrauempndxysgASDl")) != -1)
{
switch (ch) {
case 'S':
vsdbDisplay = 1;
if (argc < 3) {
(void) printf("disktool: missing device for status\n Usage: disktool -S device\n");
fflush(stdout);
goto Exit;
} else {
device = (char *) argv[2];
if (!testDevice(device)) {
goto Exit;
}
}
break;
case 's':
setEncoding = 1;
if (argc < 3) {
(void) printf("disktool: missing device for encoding\n Usage: disktool -s device encoding\n");
fflush(stdout);
goto Exit;
} else {
device = (char *) argv[2];
if (!testDevice(device)) {
goto Exit;
}
if (argv[3]) {
flags = atoi(argv[3]);
} else {
flags = 0;
}
}
break;
case 'g':
getEncoding = 1;
if (argc < 3) {
(void) printf("disktool: missing device for encoding\n Usage: disktool -g device\n");
fflush(stdout);
goto Exit;
} else {
device = (char *) argv[2];
if (!testDevice(device)) {
goto Exit;
}
}
case 'r':
refresh = 1;
break;
case 'u':
do_unmount = 1;
if (argc < 3) {
(void) printf("disktool: missing device for unmount\n Usage: disktool -u device flags\n");
fflush(stdout);
goto Exit;
} else {
device = (char *) argv[2];
if (!testDevice(device)) {
goto Exit;
}
if (argv[3]) {
flags = atoi(argv[3]);
} else {
flags = 0;
}
}
break;
case 'p':
punmount = 1;
if (argc < 3) {
(void) printf("disktool: missing device for partition unmount\n Usage: disktool -p device flags\n");
fflush(stdout);
goto Exit;
} else {
device = (char *) argv[2];
if (!testDevice(device)) {
goto Exit;
}
flags = kDiskArbUnmountOneFlag;
}
break;
case 'e':
eject = 1;
if (argc < 3) {
(void) printf("disktool: missing device for eject\n Usage: disktool -e device flags\n");
fflush(stdout);
goto Exit;
} else {
device = (char *) argv[2];
if (!testDevice(device)) {
goto Exit;
}
if (argv[3]) {
flags = atoi(argv[3]);
} else {
flags = 0;
}
}
break;
case 'A':
vsdbAdopt = 1;
if (argc < 3) {
(void) printf("disktool: missing device for adopt\n Usage: disktool -A device\n");
fflush(stdout);
goto Exit;
} else {
device = (char *) argv[2];
if (!testDevice(device)) {
goto Exit;
}
}
break;
case 'a':
afpmount = 1;
if (argc < 5) {
(void) printf("disktool: missing device, mountpoint or flags for afp mount\n AFP Usage: disktool -a device mountpoint flags\n");
fflush(stdout);
goto Exit;
} else {
device = (char *) argv[2];
if (!testDevice(device)) {
goto Exit;
}
mountpoint = (char *) argv[3];
afpflags = atoi(argv[4]);
}
break;
case 'D':
vsdbDisown = 1;
if (argc < 3) {
(void) printf("disktool: missing device for disown\n Usage: disktool -D device\n");
fflush(stdout);
goto Exit;
} else {
device = (char *) argv[2];
if (!testDevice(device)) {
goto Exit;
}
}
break;
case 'd':
afpdismount = 1;
if (argc < 3) {
(void) printf("disktool: missing device for dismount\n Usage: disktool -d device\n");
fflush(stdout);
goto Exit;
} else {
device = (char *) argv[2];
if (!testDevice(device)) {
goto Exit;
}
}
break;
case 'm':
do_mount = 1;
if (argc < 3) {
(void) printf("disktool: missing device for mount\n Usage: disktool -m device\n");
fflush(stdout);
goto Exit;
} else {
device = (char *) argv[2];
if (!testDevice(device)) {
goto Exit;
}
}
break;
case 'n':
newname = 1;
if (argc < 4) {
(void) printf("disktool: missing device or name for rename\n Usage: disktool -n device newName\n");
fflush(stdout);
goto Exit;
} else {
device = (char *) argv[2];
if (!testDevice(device)) {
goto Exit;
}
mountpoint = (char *) argv[3];
}
break;
case 'x':
refuse = 1;
break;
case 'X':
refuseMounts = 1;
break;
case 'y':
refuse = 0;
break;
case 'l':
refuse = 0;
list = 1;
break;
case 'R':
device = (char *) argv[2];
retain = 1;
break;
case 'T':
device = (char *) argv[2];
test = 1;
break;
case 'E':
device = (char *) argv[2];
release = 1;
break;
case 'U':
unrecognizedMessages = 1;
flags = atoi(argv[2]);
break;
case 'V':
hideUnrecognizedMessages = 1;
break;
case 'h':
DisplayHelp();
goto Exit;
break;
default:
exit(1);
break;
}
}
err = DiskArbStart(&diskArbitrationPort);
if (err != KERN_SUCCESS) {
printf("FAILURE: DiskArbStart() -> %d\n", err);
fflush(stdout);
goto Exit;
}
DiskArbRegisterCallback_CallFailedNotification(CallFailed_callback);
if (refuseMounts) {
DiskArbAddCallbackHandler(kDA_DISK_APPROVAL_NOTIFY, DiskApproval_callback, 0);
}
if (!do_unmount && !eject && !punmount && !vsdb && !getEncoding && !setEncoding && !retain && !test && !release && !unrecognizedMessages) {
DiskArbRegisterCallback_UnmountPreNotification(UnmountPreNotification_callback);
DiskArbRegisterCallback_UnmountPostNotification(UnmountPostNotification_callback);
DiskArbRegisterCallback_EjectPreNotification(EjectPreNotification_callback);
DiskArbRegisterCallback_EjectPostNotification(EjectPostNotification_callback);
DiskArbRegisterCallback_DiskChangedNotification(DiskChanged_callback);
DiskArbRegisterCallback_NotificationComplete(NotificationComplete_callback);
DiskArbAddCallbackHandler(kDA_DISK_APPEARED_COMPLETE, DiskAppearedComplete_callback, 0);
}
if (do_unmount || punmount || eject) {
DiskArbRegisterCallback_UnmountPreNotification(UnmountPreNotification_callback);
DiskArbRegisterCallback_UnmountPostNotification(UnmountPostNotification_callback);
DiskArbRegisterCallback_NotificationComplete(NotificationComplete_callback);
DiskArbRegisterCallback_EjectPreNotification(EjectPreNotification_callback);
DiskArbRegisterCallback_EjectPostNotification(EjectPostNotification_callback);
DiskArbRegisterCallback_CallFailedNotification(CallFailed_callback);
}
DiskArbUpdateClientFlags();
if (hideUnrecognizedMessages) {
DiskArbClientHandlesUninitializedDisks_auto(1);
}
msgSendBufLength = sizeof(mach_msg_empty_send_t) + 20;
msgSendBufPtr = (mach_msg_header_t *) malloc(msgSendBufLength);
if (msgSendBufPtr == NULL) {
printf("FAILURE: msgSendBufPtr = malloc(%d)\n", msgSendBufLength);
fflush(stdout);
goto Exit;
}
cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault,diskArbitrationPort,( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
if (!cfMachPort) {
printf("FAILURE: could not create CFMachPort\n");
goto Exit;
}
cfRunLoopSource = CFMachPortCreateRunLoopSource ( kCFAllocatorDefault, cfMachPort, 0 );
if (!cfRunLoopSource) {
printf("FAILURE: could not create CFRunLoopSource\n");
goto Exit;
}
CFRunLoopAddSource ( CFRunLoopGetCurrent ( ), cfRunLoopSource, kCFRunLoopDefaultMode );
CFRelease ( cfMachPort );
CFRelease ( cfRunLoopSource );
if (setEncoding) {
DiskArbSetVolumeEncoding_auto(device, flags);
getEncoding = 1;
}
if (getEncoding) {
int foo = DiskArbGetVolumeEncoding_auto(device);
printf("The volume encoding is %d\n", foo);
fflush(stdout);
goto Exit;
}
if (vsdbAdopt && device) {
printf("Adopting volinfo database device %s\n", device);
fflush(stdout);
DiskArbVSDBAdoptVolume_auto(device);
vsdbDisplay = 1;
}
if (vsdbDisown && device) {
printf("Disowning volinfo database device %s\n", device);
fflush(stdout);
DiskArbVSDBDisownVolume_auto(device);
vsdbDisplay = 1;
}
if (vsdbDisplay && device) {
printf("Displaying volinfo database device %s. permissions = %d\n", device, DiskArbVSDBGetVolumeStatus_auto(device));
fflush(stdout);
goto Exit;
}
if (refresh) {
printf("Refreshing Disk Arbitration ...\n");
fflush(stdout);
DiskArbRefresh_auto();
goto Exit;
}
if (retain) {
DiskArbAddCallbackHandler(kDA_DEVICE_RESERVATION_STATUS, reserveCallback, 0);
DiskArbAddCallbackHandler(kDA_WILL_CLIENT_RELEASE_DEVICE, willRelease, 0);
DiskArbRetainClientReservationForDevice(device);
}
if (test) {
DiskArbAddCallbackHandler(kDA_DEVICE_RESERVATION_STATUS, reserveCallback, 0);
DiskArbAddCallbackHandler(kDA_WILL_CLIENT_RELEASE_DEVICE, willRelease, 0);
DiskArbIsDeviceReservedForClient(device);
}
if (release) {
DiskArbReleaseClientReservationForDevice(device);
goto Exit;
}
if (unrecognizedMessages) {
printf("Waiting on unrecognized disks of all types with priority %d\n", flags);
DiskArbClientHandlesUnrecognizedDisks(kDiskArbHandlesAllUnrecognizedOrUninitializedMedia, flags);
DiskArbAddCallbackHandler(kDA_CLIENT_WILL_HANDLE_UNRECOGNIZED_DISK, unrecognizedCallback, 0);
}
if (do_unmount && device) {
printf("%s device will be unmounted ...\n", device);
fflush(stdout);
DiskArbUnmountRequest_async_auto(device, flags);
goto Loop;
}
if (punmount && device) {
printf("%s partition will attempt to be unmounted ...\n", device);
fflush(stdout);
DiskArbUnmountRequest_async_auto(device, flags);
goto Loop;
}
if (eject && device) {
printf("%s device will attempt to be ejected ...\n", device);
fflush(stdout);
DiskArbUnmountAndEjectRequest_async_auto(device, flags);
goto Loop;
}
if (do_mount && device) {
printf("%s device will attempt to be mounted ...\n", device);
fflush(stdout);
DiskArbRequestMount_auto(device);
goto Loop;
}
if (afpmount && device) {
printf("%s afp mount will attempt to be mounted ...\n", device);
fflush(stdout);
DiskArbDiskAppearedWithMountpointPing_auto(device, kDiskArbDiskAppearedNetworkDiskMask | kDiskArbDiskAppearedEjectableMask, mountpoint);
goto Exit;
}
if (afpdismount && device) {
printf("%s afp mount will attempt to be unmounted ...\n", device);
fflush(stdout);
DiskArbDiskDisappearedPing_auto(device, afpflags);
goto Exit;
}
if (newname && device && mountpoint) {
printf("Device %s will be attempt to renamed to %s ... \n", device, mountpoint);
fflush(stdout);
DiskArbRequestDiskChange_auto(device, mountpoint, 0);
}
Loop:
CFRunLoopRun();
Exit:
exit(0);
return 0;
}