Qt-interest Archive, May 2007
[PATCH] SEGV with Cups support when no cups printer available
Message 1 in thread
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi,
This patch is against 4.2.3 (X11, opensource). It fixes Issue N159995
(QPrintDialog causes segfault on Linux). To reproduce this bug:
- have Qt compiled with cups support
- cups libs locally installed and used by Qt
- local cups server stopped
- an /etc/printcap file with at least 1 entry
Then create a QPrintDialog => SEGV
This bug is related to a confusion on the meaning of
QCupsSupport::isAvailable(): for some caller functions, this meant "Cups
libraries are available", for some others it means "Cups libraries
available AND at least one cups printer reported by the server".
In case Cups libraries can be loaded BUT no cups printer is available,
Qt would crash if there is at least one printer in /etc/printcap:
_q_printerChanged will call CupsSupport::setCurrentPrinter() with the
index of an /etc/printcap printer (thinking this is the index of a cups
printer), while the array of cups printers in the the QCupsSupport
object points to NULL. SEGV.
In qpdf, another problem related to that: it will try to call the Cups
lpr, with cups options, instead of the basic LPD client. This lp client
would refuse these options (eg some "-o" options).
What this patch does is change the meaning of isAvailable() to "Cups
libraries available AND at least one cups printer reported by the
server". The connection to the server is checked:
- the 1st time the function is called
- each time a QCupsSupport object is created (eg each time a
QPrintDialog box is created). This is what the new "retry_connection"
parameter (false by default) is for.
This is not really nice because, for example in the QCupsSupport ctor,
cupsGetDests() is called 2 successive times.
Added to the patch:
- for lpr-style printing: support for PRINTER, LPDEST, NPRINTER and
NGPRINTER environment variables to select the default printer
- for /etc/printcap-style printing: printer names are sorted in the
dialog box
- RHEL4 only (?): lpr is tried before lp (on RHEL4, lp does not
accept the options passed by Qt, so we try lpr first)
I submitted this message to the bug tracker, but I'm not sure it was the
best procedure.
Hope this helps.
Bye,
- --
David Decotigny
GLAST/ISOC -- Ground Computer Science Staff
Building: 084, Office: B190, Ext: 3427
SLAC, Menlo Park, CA 94025, USA
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFGS5OTld7vhusVrCERAgIsAJ4xcN0pNYAnAeiR2LAhQCiKNLuFNgCfTGj7
rx/oFHyd8lwJtiDsr0sxkRg=
=IDWz
-----END PGP SIGNATURE-----
Tried to solve Issue N159995 (QPrintDialog causes segfault on Linux). This was related to a confusion on the meaning of QCupsSupport::isAvailable(): for some functions, this meant "Cups libraries are available", for some others it means "Cups libraries available AND at least one cups printer reported by the server". In case Cups libraries can be loaded BUT no cups printer is available, Qt would crash if there is at least one printer in /etc/printcap for example: _q_printerChanged will call CupsSupport::setCurrentPrinter() with the index of an /etc/printcap printer (thinking this is the index of a cups printer), while the the array of cups printers in the the QCupsSupport object points to NULL. SEGV.
In qpdf, another problem related to that: it will try to call the Cups lpr, with cups options, instead of the basic LPD client. This lp client would refuse these options.
What this patch does is change the meaning of isAvailable() to "Cups libraries available AND at least one cups printer reported by the server". The connection to the server is checked:
- the 1st time the function is called
- each time a QCupsSupport object is created (eg each time a QPrintDialog box is created). This is what the new "retry_connection" parameter (false by default) is for.
This is not really nice because, for example in the QCupsSupport ctor, cupsGetDests() is called 2 successive times.
Added to the patch:
- for lpr-style printing: support for PRINTER, LPDEST, NPRINTER and NGPRINTER environment variables to select the default printer
- for /etc/printcap-style printing: printer names are sorted in the dialog box
- lpr is checked before lp (on RHEL4, lp does not accept the options passed by Qt, so we try lpr first)
--- qt-x11-opensource-src-4.2.3/src/gui/dialogs/qprintdialog_unix.cpp.isoc-prn 2007-02-21 01:58:46.000000000 -0800
+++ qt-x11-opensource-src-4.2.3/src/gui/dialogs/qprintdialog_unix.cpp 2007-05-16 15:22:24.000000000 -0700
@@ -82,6 +82,14 @@
}
};
+
+static bool printerNameLessThan(const QPrinterDescription & p1,
+ const QPrinterDescription & p2)
+{
+ return p1.name < p2.name;
+}
+
+
class QPrintDialogPrivate : public QAbstractPrintDialogPrivate
{
Q_DECLARE_PUBLIC(QPrintDialog)
@@ -275,6 +283,9 @@
printerDesc = line;
}
delete[] line_ascii;
+
+ // Sort the printers' list in lexicographic order
+ qSort(printers->begin(), printers->end(), printerNameLessThan);
return Success;
}
@@ -853,12 +864,17 @@
// all printers hopefully known. try to find a good default
QString dollarPrinter;
{
- if (!qgetenv("PRINTER").isEmpty())
- dollarPrinter = QString::fromLocal8Bit(qgetenv("LPDEST").constData());
- if (!dollarPrinter.isEmpty())
- perhapsAddPrinter(&printers, dollarPrinter,
- QPrintDialog::tr("unknown"),
- QLatin1String(""));
+ dollarPrinter = QString::fromLocal8Bit(qgetenv("PRINTER").constData());
+ if (dollarPrinter.isEmpty())
+ dollarPrinter = QString::fromLocal8Bit(qgetenv("LDPDEST").constData());
+ if (dollarPrinter.isEmpty())
+ dollarPrinter = QString::fromLocal8Bit(qgetenv("NPRINTER").constData());
+ if (dollarPrinter.isEmpty())
+ dollarPrinter = QString::fromLocal8Bit(qgetenv("NGPRINTER").constData());
+ if (!dollarPrinter.isEmpty())
+ perhapsAddPrinter(&printers, dollarPrinter,
+ QPrintDialog::tr("unknown"),
+ QLatin1String(""));
}
int quality = 0;
@@ -917,7 +933,7 @@
#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
cups = new QCUPSSupport;
- if (QCUPSSupport::isAvailable() && cups->availablePrintersCount() > 0) {
+ if (QCUPSSupport::isAvailable()) {
cupsPPD = cups->currentPPD();
cupsPrinterCount = cups->availablePrintersCount();
cupsPrinters = cups->availablePrinters();
@@ -1093,7 +1109,8 @@
ui.cbPaperSize->clear();
#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
- if (QCUPSSupport::isAvailable() && ui.chbPrintToFile->checkState() != Qt::Checked) {
+ if (QCUPSSupport::isAvailable()
+ && (ui.chbPrintToFile->checkState() != Qt::Checked)) {
const ppd_option_t* pageSizes = cups->pageSizes();
int numChoices = pageSizes ? pageSizes->num_choices : 0;
@@ -1192,7 +1209,8 @@
ps = val.toInt();
}
#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
- else if (QCUPSSupport::isAvailable() && ui.chbPrintToFile->checkState() != Qt::Checked
+ else if (QCUPSSupport::isAvailable()
+ && (ui.chbPrintToFile->checkState() != Qt::Checked)
&& cups->currentPPD()) {
QByteArray cupsPageSize = val.toByteArray();
QPrintEngine *engine = p->printEngine();
--- qt-x11-opensource-src-4.2.3/src/gui/painting/qpdf.cpp.isoc-prn 2007-05-16 11:16:03.000000000 -0700
+++ qt-x11-opensource-src-4.2.3/src/gui/painting/qpdf.cpp 2007-05-16 15:03:05.000000000 -0700
@@ -1537,12 +1537,12 @@
for (int i = 0; i < lprhack.size(); ++i)
lprargs[i+1] = (char *)lprhack[i].constData();
lprargs[lprhack.size() + 1] = 0;
- (void)execvp("lp", lpargs);
(void)execvp("lpr", lprargs);
- (void)execv("/bin/lp", lpargs);
+ (void)execvp("lp", lpargs);
(void)execv("/bin/lpr", lprargs);
- (void)execv("/usr/bin/lp", lpargs);
+ (void)execv("/bin/lp", lpargs);
(void)execv("/usr/bin/lpr", lprargs);
+ (void)execv("/usr/bin/lp", lpargs);
}
// if we couldn't exec anything, close the fd,
// wait for a second so the parent process (the
--- qt-x11-opensource-src-4.2.3/src/gui/painting/qcups_p.h.isoc-prn 2007-05-16 11:31:58.000000000 -0700
+++ qt-x11-opensource-src-4.2.3/src/gui/painting/qcups_p.h 2007-05-16 11:32:08.000000000 -0700
@@ -48,7 +48,7 @@
QCUPSSupport();
~QCUPSSupport();
- static bool isAvailable();
+ static bool isAvailable(bool retry_connection = false);
static int cupsVersion() { return isAvailable() ? CUPS_VERSION_MAJOR*10000+CUPS_VERSION_MINOR*100+CUPS_VERSION_PATCH : 0; }
int availablePrintersCount() const;
const cups_dest_t* availablePrinters() const;
--- qt-x11-opensource-src-4.2.3/src/gui/painting/qcups.cpp.isoc-prn 2007-02-21 01:58:43.000000000 -0800
+++ qt-x11-opensource-src-4.2.3/src/gui/painting/qcups.cpp 2007-05-16 15:27:28.000000000 -0700
@@ -26,6 +26,7 @@
#ifndef QT_NO_CUPS
typedef int (*CupsGetDests)(cups_dest_t **dests);
+typedef void (*CupsFreeDests)(int num_dests, cups_dest_t *dests);
typedef const char* (*CupsGetPPD)(const char *printer);
typedef int (*CupsMarkOptions)(ppd_file_t *ppd, int num_options, cups_option_t *options);
typedef ppd_file_t* (*PPDOpenFile)(const char *filename);
@@ -37,8 +38,10 @@
typedef void (*CupsSetDests)(int num_dests, cups_dest_t *dests);
typedef int (*CupsAddOption)(const char *name, const char *value, int num_options, cups_option_t **options);
-static bool cupsLoaded = false;
+static bool cupsLoaded = false;
+static bool cupsAvailable = false;
static CupsGetDests _cupsGetDests = 0;
+static CupsFreeDests _cupsFreeDests = 0;
static CupsGetPPD _cupsGetPPD = 0;
static PPDOpenFile _ppdOpenFile = 0;
static PPDMarkDefaults _ppdMarkDefaults = 0;
@@ -54,6 +57,7 @@
QLibrary cupsLib(QLatin1String("cups"), 2);
if(cupsLib.load()) {
_cupsGetDests = (CupsGetDests) cupsLib.resolve("cupsGetDests");
+ _cupsFreeDests = (CupsFreeDests) cupsLib.resolve("cupsFreeDests");
_cupsGetPPD = (CupsGetPPD) cupsLib.resolve("cupsGetPPD");
_ppdOpenFile = (PPDOpenFile) cupsLib.resolve("ppdOpenFile");
_ppdMarkDefaults = (PPDMarkDefaults) cupsLib.resolve("ppdMarkDefaults");
@@ -77,11 +81,8 @@
currPrinterIndex(0),
currPPD(0)
{
- if(!cupsLoaded)
- resolveCups();
-
// getting all available printers
- if (isAvailable())
+ if (isAvailable(true))
prnCount = _cupsGetDests(&printers);
for (int i = 0; i < prnCount; ++i) {
@@ -97,6 +98,8 @@
{
if (currPPD)
_ppdClose(currPPD);
+ if (printers)
+ _cupsFreeDests(prnCount, printers);
}
int QCUPSSupport::availablePrintersCount() const
@@ -152,20 +155,38 @@
return currPrinterIndex;
}
-bool QCUPSSupport::isAvailable()
+bool QCUPSSupport::isAvailable(bool retry_connection)
{
if(!cupsLoaded)
+ {
resolveCups();
- return _cupsGetDests &&
- _cupsGetPPD &&
- _ppdOpenFile &&
- _ppdMarkDefaults &&
- _ppdClose &&
- _cupsMarkOptions &&
- _ppdMarkOption &&
- _cupsFreeOptions &&
- _cupsSetDests &&
- _cupsAddOption;
+ retry_connection = true;
+ }
+
+ if (!_cupsGetDests ||
+ !_cupsFreeDests ||
+ !_cupsGetPPD ||
+ !_ppdOpenFile ||
+ !_ppdMarkDefaults ||
+ !_ppdClose ||
+ !_cupsMarkOptions ||
+ !_ppdMarkOption ||
+ !_cupsFreeOptions ||
+ !_cupsSetDests ||
+ !_cupsAddOption)
+ return false;
+
+ if (retry_connection)
+ {
+ /* Retrieve the number of printers actually available */
+ cups_dest_t * dests = NULL;
+ int nbpr = _cupsGetDests(& dests);
+ if (dests)
+ _cupsFreeDests(nbpr, dests);
+ cupsAvailable = (nbpr > 0);
+ }
+
+ return cupsAvailable;
}
const ppd_option_t* QCUPSSupport::ppdOption(const char *key) const
Message 2 in thread
Hi,
David Decotigny wrote:
> This patch is against 4.2.3 (X11, opensource). It fixes Issue N159995
> (QPrintDialog causes segfault on Linux). To reproduce this bug:
> [...]
Please report bugs and send patches here:
http://www.trolltech.com/developer/bugreport-form
That's the only way to make sure the bug is known and eventually fixed.
--
[ signature omitted ]