/*
-* "$Id: usb-darwin.c 7460 2008-04-16 02:19:54Z mike $"
+* "$Id: usb-darwin.c 7953 2008-09-17 01:43:19Z mike $"
*
-* Copyright 2005-2008 Apple Inc. All rights reserved.
+* Copyright 2005-2009 Apple Inc. All rights reserved.
*
* IMPORTANT: This Apple software is supplied to you by Apple Computer,
* Inc. ("Apple") in consideration of your agreement to the following
extern char **environ;
+/*
+ * DEBUG_WRITES, if defined, causes the backend to write data to the printer in
+ * 512 byte increments, up to 8192 bytes, to make debugging with a USB bus
+ * analyzer easier.
+ */
+
+#define DEBUG_WRITES 0
+
/*
* WAIT_EOF_DELAY is number of seconds we'll wait for responses from
* the printer after we've finished sending all the data
int print_fd; /* File descriptor to print */
ssize_t print_bytes; /* Print bytes read */
+#if DEBUG_WRITES
+ ssize_t debug_bytes; /* Current bytes to read */
+#endif /* DEBUG_WRITES */
Boolean wait_eof;
int drain_output; /* Drain all pending output */
static void status_timer_cb(CFRunLoopTimerRef timer, void *info);
#if defined(__i386__) || defined(__x86_64__)
-static pid_t child_pid; /* Child PID */
+static pid_t child_pid; /* Child PID */
static void run_legacy_backend(int argc, char *argv[], int fd); /* Starts child backend process running as a ppc executable */
-static void sigterm_handler(int sig); /* SIGTERM handler */
+static void sigterm_handler(int sig); /* SIGTERM handler */
#endif /* __i386__ || __x86_64__ */
#ifdef PARSE_PS_ERRORS
{
char serial[1024]; /* Serial number buffer */
OSStatus status; /* Function results */
+ IOReturn iostatus; /* Current IO status */
pthread_t read_thread_id, /* Read thread */
sidechannel_thread_id;/* Side-channel thread */
int have_sidechannel = 0; /* Was the side-channel thread started? */
{
fputs("DEBUG: Received an interrupt before any bytes were "
"written, aborting!\n", stderr);
- return (0);
+ return (CUPS_BACKEND_OK);
}
- else if (errno != EAGAIN)
+ else if (errno != EAGAIN && errno != EINTR)
{
_cupsLangPuts(stderr, _("ERROR: Unable to read print data!\n"));
perror("DEBUG: select");
- return (CUPS_BACKEND_STOP);
+ return (CUPS_BACKEND_FAILED);
}
}
if (FD_ISSET(print_fd, &input_set))
{
+#if DEBUG_WRITES
+ g.debug_bytes += 512;
+ if (g.debug_bytes > sizeof(print_buffer))
+ g.debug_bytes = 512;
+
+ g.print_bytes = read(print_fd, print_buffer, g.debug_bytes);
+
+#else
g.print_bytes = read(print_fd, print_buffer, sizeof(print_buffer));
+#endif /* DEBUG_WRITES */
if (g.print_bytes < 0)
{
* Read error - bail if we don't see EAGAIN or EINTR...
*/
- if (errno != EAGAIN || errno != EINTR)
+ if (errno != EAGAIN && errno != EINTR)
{
_cupsLangPuts(stderr, _("ERROR: Unable to read print data!\n"));
perror("DEBUG: read");
- return CUPS_BACKEND_STOP;
+ return (CUPS_BACKEND_FAILED);
}
g.print_bytes = 0;
if (g.print_bytes)
{
- bytes = g.print_bytes;
+ bytes = g.print_bytes;
+ iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
- status = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
+ /*
+ * Ignore timeout errors, but retain the number of bytes written to
+ * avoid sending duplicate data (<rdar://problem/6254911>)...
+ */
+
+ if (iostatus == kIOUSBTransactionTimeout)
+ {
+ fputs("DEBUG: Got USB transaction timeout during write!\n", stderr);
+ iostatus = 0;
+ }
/*
- * Ignore timeout errors...
+ * If we've stalled, retry the write...
*/
- if (status == kIOUSBTransactionTimeout)
+ else if (iostatus == kIOUSBPipeStalled)
{
- status = 0;
- bytes = 0;
+ fputs("DEBUG: Got USB pipe stalled during write!\n", stderr);
+
+ bytes = g.print_bytes;
+ iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
}
- if (status || bytes < 0)
+ /*
+ * Retry a write after an aborted write since we probably just got
+ * SIGTERM (<rdar://problem/6860126>)...
+ */
+
+ else if (iostatus == kIOReturnAborted)
+ {
+ fputs("DEBUG: Got return aborted during write!\n", stderr);
+
+ IOReturn err = (*g.classdriver)->Abort(g.classdriver);
+ fprintf(stderr, "DEBUG: USB class driver Abort returned %x\n", err);
+
+#if DEBUG_WRITES
+ sleep(5);
+#endif /* DEBUG_WRITES */
+
+ bytes = g.print_bytes;
+ iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
+ }
+
+ if (iostatus || bytes < 0)
{
/*
* Write error - bail if we don't see an error we can retry...
*/
- OSStatus err = (*g.classdriver)->Abort(g.classdriver);
_cupsLangPuts(stderr, _("ERROR: Unable to send print data!\n"));
- fprintf(stderr, "DEBUG: USB class driver WritePipe returned %ld\n",
- (long)status);
- fprintf(stderr, "DEBUG: USB class driver Abort returned %ld\n",
- (long)err);
- status = CUPS_BACKEND_STOP;
+ fprintf(stderr, "DEBUG: USB class driver WritePipe returned %x\n",
+ iostatus);
+
+ IOReturn err = (*g.classdriver)->Abort(g.classdriver);
+ fprintf(stderr, "DEBUG: USB class driver Abort returned %x\n",
+ err);
+
+ status = CUPS_BACKEND_FAILED;
break;
}
+ else if (bytes > 0)
+ {
+ fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", (int)bytes);
- fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", (int)bytes);
-
- g.print_bytes -= bytes;
- print_ptr += bytes;
- total_bytes += bytes;
+ g.print_bytes -= bytes;
+ print_ptr += bytes;
+ total_bytes += bytes;
+ }
}
if (print_fd != 0 && status == noErr)
* Force the read thread to exit...
*/
+ g.wait_eof = 0;
pthread_kill(read_thread_id, SIGTERM);
}
}
readstatus = (*g.classdriver)->ReadPipe(g.classdriver, readbuffer, &rbytes);
if (readstatus == kIOReturnSuccess && rbytes > 0)
{
+ fprintf(stderr, "DEBUG: Read %d bytes of back-channel data...\n",
+ (int)rbytes);
cupsBackChannelWrite((char*)readbuffer, rbytes, 1.0);
/* cntrl-d is echoed by the printer.
parse_pserror(readbuffer, rbytes);
#endif
}
+ else if (readstatus == kIOUSBTransactionTimeout)
+ fputs("DEBUG: Got USB transaction timeout during write!\n", stderr);
+ else if (readstatus == kIOUSBPipeStalled)
+ fputs("DEBUG: Got USB pipe stalled during read!\n", stderr);
+ else if (readstatus == kIOReturnAborted)
+ fputs("DEBUG: Got return aborted during read!\n", stderr);
/*
* Make sure this loop executes no more than once every 250 miliseconds...
datalen = sizeof(data);
if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
- continue;
+ break;
switch (command)
{
case CUPS_SC_CMD_SOFT_RESET: /* Do a soft reset */
+ fputs("DEBUG: CUPS_SC_CMD_SOFT_RESET received from driver...\n",
+ stderr);
+
if ((*g.classdriver)->SoftReset != NULL)
{
soft_reset();
cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, NULL, 0, 1.0);
+ fputs("DEBUG: Returning status CUPS_STATUS_OK with no bytes...\n",
+ stderr);
}
else
{
cupsSideChannelWrite(command, CUPS_SC_STATUS_NOT_IMPLEMENTED,
NULL, 0, 1.0);
+ fputs("DEBUG: Returning status CUPS_STATUS_NOT_IMPLEMENTED with "
+ "no bytes...\n", stderr);
}
break;
case CUPS_SC_CMD_DRAIN_OUTPUT: /* Drain all pending output */
+ fputs("DEBUG: CUPS_SC_CMD_DRAIN_OUTPUT received from driver...\n",
+ stderr);
+
g.drain_output = 1;
break;
case CUPS_SC_CMD_GET_BIDI: /* Is the connection bidirectional? */
+ fputs("DEBUG: CUPS_SC_CMD_GET_BIDI received from driver...\n",
+ stderr);
+
data[0] = g.bidi_flag;
cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0);
+
+ fprintf(stderr,
+ "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n",
+ data[0]);
break;
case CUPS_SC_CMD_GET_DEVICE_ID: /* Return IEEE-1284 device ID */
+ fputs("DEBUG: CUPS_SC_CMD_GET_DEVICE_ID received from driver...\n",
+ stderr);
+
datalen = sizeof(data);
get_device_id(&status, data, &datalen);
cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, datalen, 1.0);
+
+ if (datalen < sizeof(data))
+ data[datalen] = '\0';
+ else
+ data[sizeof(data) - 1] = '\0';
+
+ fprintf(stderr,
+ "DEBUG: Returning CUPS_SC_STATUS_OK with %d bytes (%s)...\n",
+ datalen, data);
break;
case CUPS_SC_CMD_GET_STATE: /* Return device state */
+ fputs("DEBUG: CUPS_SC_CMD_GET_STATE received from driver...\n",
+ stderr);
+
data[0] = CUPS_SC_STATE_ONLINE;
cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0);
+
+ fprintf(stderr,
+ "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n",
+ data[0]);
break;
default:
+ fprintf(stderr, "DEBUG: Unknown side-channel command (%d) received "
+ "from driver...\n", command);
+
cupsSideChannelWrite(command, CUPS_SC_STATUS_NOT_IMPLEMENTED,
NULL, 0, 1.0);
+
+ fputs("DEBUG: Returned CUPS_SC_STATUS_NOT_IMPLEMENTED with no bytes...\n",
+ stderr);
break;
}
}
modelstr[0] = '/';
- if (!CFStringGetCString(make, makestr, sizeof(makestr),
+ if (!make ||
+ !CFStringGetCString(make, makestr, sizeof(makestr),
kCFStringEncodingUTF8))
strcpy(makestr, "Unknown");
- if (!CFStringGetCString(model, &modelstr[1], sizeof(modelstr)-1,
+ if (!model ||
+ !CFStringGetCString(model, &modelstr[1], sizeof(modelstr)-1,
kCFStringEncodingUTF8))
strcpy(modelstr + 1, "Printer");
snprintf(optionsstr, sizeof(optionsstr), "?location=%x", (unsigned)deviceLocation);
httpAssembleURI(HTTP_URI_CODING_ALL, uristr, sizeof(uristr), "usb", NULL, makestr, 0, modelstr);
- strncat(uristr, optionsstr, sizeof(uristr));
+ strlcat(uristr, optionsstr, sizeof(uristr));
cupsBackendReport("direct", uristr, make_modelstr, make_modelstr, idstr,
NULL);
CFStringRef serialKeys[] = { CFSTR("SN:"), CFSTR("SERN:"), NULL };
if (make != NULL)
- {
- if ((*make = copy_value_for_key(deviceIDString, makeKeys)) == NULL)
- *make = CFStringCreateWithCString(kCFAllocatorDefault, "Unknown",
- kCFStringEncodingUTF8);
- }
+ *make = copy_value_for_key(deviceIDString, makeKeys);
if (model != NULL)
- {
- if ((*model = copy_value_for_key(deviceIDString, modelKeys)) == NULL)
- *model = CFStringCreateWithCString(kCFAllocatorDefault, "Printer",
- kCFStringEncodingUTF8);
- }
+ *model = copy_value_for_key(deviceIDString, modelKeys);
if (serial != NULL)
*serial = copy_value_for_key(deviceIDString, serialKeys);
printer_interface_t intf,
classdriver_t ***printerDriver)
{
- kern_return_t kr = kUSBPrinterClassDeviceNotOpen;
- classdriver_t **driver = NULL;
- CFStringRef bundle = (driverPath == NULL ? kUSBGenericTOPrinterClassDriver : driverPath);
+ kern_return_t kr = kUSBPrinterClassDeviceNotOpen;
+ classdriver_t **driver = NULL;
+ CFStringRef bundle = driverPath ? driverPath : kUSBGenericTOPrinterClassDriver;
+ char bundlestr[1024]; /* Bundle path */
+ struct stat bundleinfo; /* File information for bundle */
+ CFURLRef url; /* URL for driver */
+ CFPlugInRef plugin = NULL; /* Plug-in address */
+
+
+ CFStringGetCString(bundle, bundlestr, sizeof(bundlestr), kCFStringEncodingUTF8);
- if (bundle != NULL)
+ /*
+ * Validate permissions for the class driver...
+ */
+
+ if (stat(bundlestr, &bundleinfo))
+ {
+ fprintf(stderr, "DEBUG: Unable to load class driver \"%s\": %s\n",
+ bundlestr, strerror(errno));
+ return (kr);
+ }
+ else if (bundleinfo.st_mode & S_IWOTH)
{
- CFURLRef url = CFURLCreateWithFileSystemPath(NULL, bundle, kCFURLPOSIXPathStyle, true);
- CFPlugInRef plugin = (url != NULL ? CFPlugInCreate(NULL, url) : NULL);
+ fprintf(stderr, "DEBUG: Unable to load class driver \"%s\": insecure file "
+ "permissions (0%o)\n", bundlestr, bundleinfo.st_mode);
+ return (kr);
+ }
+
+ /*
+ * Try loading the class driver...
+ */
- if (url != NULL)
- CFRelease(url);
+ url = CFURLCreateWithFileSystemPath(NULL, bundle, kCFURLPOSIXPathStyle, true);
- if (plugin != NULL)
+ if (url)
+ {
+ plugin = CFPlugInCreate(NULL, url);
+ CFRelease(url);
+ }
+ else
+ plugin = NULL;
+
+ if (plugin)
+ {
+ CFArrayRef factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn(kUSBPrinterClassTypeID, plugin);
+ if (factories != NULL && CFArrayGetCount(factories) > 0)
{
- CFArrayRef factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn(kUSBPrinterClassTypeID, plugin);
- if (factories != NULL && CFArrayGetCount(factories) > 0)
+ CFUUIDRef factoryID = CFArrayGetValueAtIndex(factories, 0);
+ IUnknownVTbl **iunknown = CFPlugInInstanceCreate(NULL, factoryID, kUSBPrinterClassTypeID);
+ if (iunknown != NULL)
{
- CFUUIDRef factoryID = CFArrayGetValueAtIndex(factories, 0);
- IUnknownVTbl **iunknown = CFPlugInInstanceCreate(NULL, factoryID, kUSBPrinterClassTypeID);
- if (iunknown != NULL)
+ kr = (*iunknown)->QueryInterface(iunknown, CFUUIDGetUUIDBytes(kUSBPrinterClassInterfaceID), (LPVOID *)&driver);
+ if (kr == kIOReturnSuccess && driver != NULL)
{
- kr = (*iunknown)->QueryInterface(iunknown, CFUUIDGetUUIDBytes(kUSBPrinterClassInterfaceID), (LPVOID *)&driver);
- if (kr == kIOReturnSuccess && driver != NULL)
- {
- classdriver_t **genericDriver = NULL;
- if (driverPath != NULL && CFStringCompare(driverPath, kUSBGenericTOPrinterClassDriver, 0) != kCFCompareEqualTo)
- kr = load_classdriver(NULL, intf, &genericDriver);
+ classdriver_t **genericDriver = NULL;
+ if (driverPath != NULL && CFStringCompare(driverPath, kUSBGenericTOPrinterClassDriver, 0) != kCFCompareEqualTo)
+ kr = load_classdriver(NULL, intf, &genericDriver);
- if (kr == kIOReturnSuccess)
- {
- (*driver)->interface = intf;
- (*driver)->Initialize(driver, genericDriver);
+ if (kr == kIOReturnSuccess)
+ {
+ (*driver)->interface = intf;
+ (*driver)->Initialize(driver, genericDriver);
- (*driver)->plugin = plugin;
- (*driver)->interface = intf;
- *printerDriver = driver;
- }
+ (*driver)->plugin = plugin;
+ (*driver)->interface = intf;
+ *printerDriver = driver;
}
- (*iunknown)->Release(iunknown);
}
- CFRelease(factories);
+ (*iunknown)->Release(iunknown);
}
+ CFRelease(factories);
}
}
-#ifdef DEBUG
- char bundlestr[1024];
- CFStringGetCString(bundle, bundlestr, sizeof(bundlestr), kCFStringEncodingUTF8);
fprintf(stderr, "DEBUG: load_classdriver(%s) (kr:0x%08x)\n", bundlestr, (int)kr);
-#endif /* DEBUG */
- return kr;
+ return (kr);
}
lang[0] = CFStringCreateWithCString(kCFAllocatorDefault, requestedLang, kCFStringEncodingUTF8);
langArray = CFArrayCreate(kCFAllocatorDefault, (const void **)lang, sizeof(lang) / sizeof(lang[0]), &kCFTypeArrayCallBacks);
- CFPreferencesSetAppValue(CFSTR("AppleLanguages"), langArray, kCFPreferencesCurrentApplication);
+ CFPreferencesSetValue(CFSTR("AppleLanguages"), langArray, kCFPreferencesCurrentApplication, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
fprintf(stderr, "DEBUG: usb: AppleLanguages=\"%s\"\n", requestedLang);
CFRelease(lang[0]);
struct sigaction action; /* POSIX signal action */
sigset_t newmask, /* New signal mask */
oldmask; /* Old signal mask */
+ char usbpath[1024]; /* Path to USB backend */
+ const char *cups_serverbin;/* Path to CUPS binaries */
+
memset(&action, 0, sizeof(action));
sigaddset(&action.sa_mask, SIGTERM);
if (!posix_spawnattr_init(&attrs))
{
- posix_spawnattr_setsigdefault(attrs, &oldmask);
- if (posix_spawnattr_setbinpref_np(attrs, 1, &cpu, &ocount) || ocount != 1)
+ posix_spawnattr_setsigdefault(&attrs, &oldmask);
+ if (posix_spawnattr_setbinpref_np(&attrs, 1, &cpu, &ocount) || ocount != 1)
{
# ifdef __x86_64__
perror("DEBUG: Unable to set binary preference to i386");
* Set up the arguments and call posix_spawn...
*/
+ if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
+ cups_serverbin = CUPS_SERVERBIN;
+ snprintf(usbpath, sizeof(usbpath), "%s/backend/usb", cups_serverbin);
+
for (i = 0; i < argc && i < (sizeof(my_argv) / sizeof(my_argv[0])) - 1; i ++)
my_argv[i] = argv[i];
my_argv[i] = NULL;
- if (posix_spawn(&child_pid, "/usr/libexec/cups/backend/usb", NULL, &attrs,
- my_argv, environ))
+ if (posix_spawn(&child_pid, usbpath, NULL, &attrs, my_argv, environ))
{
- perror("DEBUG: Unable to exec /usr/libexec/cups/backend/usb");
+ fprintf(stderr, "DEBUG: Unable to exec %s: %s\n", usbpath,
+ strerror(errno));
_cupsLangPrintf(stderr, _("Unable to use legacy USB class driver!\n"));
exit(CUPS_BACKEND_STOP);
}
* 'sigterm_handler()' - SIGTERM handler.
*/
-static void sigterm_handler(int sig)
+static void
+sigterm_handler(int sig) /* I - Signal */
{
/* If we started a child process pass the signal on to it...
*/
if (child_pid)
+ {
+ /*
+ * If we started a child process pass the signal on to it...
+ */
+
+ int status;
+
kill(child_pid, sig);
+ while (waitpid(child_pid, &status, 0) < 0 && errno == EINTR);
- exit(1);
+ if (WIFEXITED(status))
+ exit(WEXITSTATUS(status));
+ else if (status == SIGTERM || status == SIGKILL)
+ exit(0);
+ else
+ {
+ fprintf(stderr, "DEBUG: Child crashed on signal %d!\n", status);
+ exit(CUPS_BACKEND_STOP);
+ }
+ }
}
#endif /* __i386__ || __x86_64__ */
char *data,
int *datalen)
{
- UInt32 deviceLocation = 0;
- UInt8 interfaceNum = 0;
CFStringRef deviceIDString = NULL;
/* GetDeviceID */
- copy_devicestring(g.printer_obj, &deviceIDString, &deviceLocation, &interfaceNum);
+ copy_deviceid(g.classdriver, &deviceIDString);
+
if (deviceIDString)
{
CFStringGetCString(deviceIDString, data, *datalen, kCFStringEncodingUTF8);
/*
- * End of "$Id: usb-darwin.c 7460 2008-04-16 02:19:54Z mike $".
+ * End of "$Id: usb-darwin.c 7953 2008-09-17 01:43:19Z mike $".
*/