[Libvir] take 2 [Re: write(2) may write less than the total requested

2008-02-20 Thread Jim Meyering
Daniel Veillard [EMAIL PROTECTED] wrote:

 On Wed, Feb 20, 2008 at 02:53:49PM +0100, Jim Meyering wrote:
 Use safewrite in place of write, in many cases.
 And add make syntax-check rules to ensure no new uses sneak in.

 There are many uses of write like this:

 if (write (fd, xml, towrite) != towrite)
 return -1;

 The problem is that the syscall can succeed, yet write less than
 the requested number of bytes, so the caller should retry
 rather than simply failing.

   agreed using safewrite is a good idea, +1

 This patch changes most of them to use util.c's safewrite wrapper,
 which encapsulates the process.  Also, there were a few cases in
 which the retry loop was open-coded, and I replaced those, too.

 However, there remain invalid uses of write in both virsh.c and
 console.c.  I would have replaced those, too, but util.c isn't
 linked with them, so it would have made this change more involved.
 xend_internal.c has one use, but it's ok, and not easily replaced.

   maybe for case outside of the library adding a copy of safewrite
 in virsh.c would be a good way to do this while keeping the code
 consistent. The duplication of that small function whould not
 be a real problem IMHO

Thanks for the quick review.
That would work well.

It's needed also for qemud.c, which already includes util.h
and uses functions like __virStrToLong_i, yet linking libvirtd
would currently fail due to the uses of safewrite.

If I do as you suggest and move the safewrite definition into util.h,
and make it static inline, then both problems go away.

Here's a new version of the patch, adjusted accordingly:

---
Use safewrite in place of write, in many cases.

And add make syntax-check rules to ensure no new uses sneak in.

There are many uses of write like this:

if (write (fd, xml, towrite) != towrite)
return -1;

The problem is that the syscall can succeed, yet write less than
the requested number of bytes, so the caller should retry
rather than simply failing.

This patch changes most of them to use util.c's safewrite wrapper,
which encapsulates the process.  Also, there were a few cases in
which the retry loop was open-coded, and I replaced those, too.
However, there remain invalid uses of write in both virsh.c and
console.c.  I would have replaced those, too, but util.c isn't
linked with them, so it would have made this change more involved.
xend_internal.c has one use, but it's ok, and not easily replaced.

Note: the change to libvirt_proxy.c removes a diagnostic that
would have warned about an EINTR-interrupted (and retried) write,
but that seems ok.

* Makefile.maint (sc_avoid_write): New rule, to avoid recurrence.
* .x-sc_avoid_write: New file.  Record two legitimate exemptions.
* qemud/qemud.c (sig_handler, qemudClientWriteBuf): Use safewrite, not write.
* src/conf.c (__virConfWriteFile): Likewise.
* src/qemu_conf.c (qemudSaveConfig, qemudSaveNetworkConfig): Likewise.
* src/qemu_driver.c (qemudWaitForMonitor, qemudStartVMDaemon)
(qemudVMData, PROC_IP_FORWARD): Likewise.
* proxy/libvirt_proxy.c: Include util.h.
(proxyWriteClientSocket): Use safewrite.
* src/test.c (testDomainSave, testDomainCoreDump): Likewise.
* src/proxy_internal.c (virProxyWriteClientSocket): Likewise.
* src/virsh.c (vshOutputLogFile): Likewise.
* src/console.c (vshRunConsole): Likewise.
* src/util.c (safewrite): Move function definition to ...
* src/util.h (safewrite): ..here, and make it static inline.
* proxy/Makefile.am: Split a long line, and add a newline at EOF.

Signed-off-by: Jim Meyering [EMAIL PROTECTED]
---
 .x-sc_avoid_write |2 ++
 Makefile.maint|   11 +++
 proxy/Makefile.am |5 +++--
 proxy/libvirt_proxy.c |   16 +---
 qemud/qemud.c |   10 --
 src/conf.c|2 +-
 src/console.c |6 --
 src/proxy_internal.c  |   14 --
 src/qemu_conf.c   |4 ++--
 src/qemu_driver.c |   19 +++
 src/test.c|9 +
 src/util.c|   22 +-
 src/util.h|   21 -
 src/virsh.c   |2 +-
 14 files changed, 70 insertions(+), 73 deletions(-)
 create mode 100644 .x-sc_avoid_write

diff --git a/.x-sc_avoid_write b/.x-sc_avoid_write
new file mode 100644
index 000..72a196d
--- /dev/null
+++ b/.x-sc_avoid_write
@@ -0,0 +1,2 @@
+^src/util\.c$
+^src/xend_internal\.c$
diff --git a/Makefile.maint b/Makefile.maint
index 4b54baf..afb 100644
--- a/Makefile.maint
+++ b/Makefile.maint
@@ -46,6 +46,17 @@ sc_avoid_if_before_free:
  { echo '$(ME): found useless if before free above' 12;  \
exit 1; } || :

+# Avoid uses of write(2).  Either switch to streams (fwrite), or use
+# the safewrite wrapper.
+sc_avoid_write:
+   @if $(CVS_LIST_EXCEPT) | grep '\.c$$'  /dev/null; then \
+ grep '\write *(' $$($(CVS_LIST_EXCEPT) | grep '\.c$$') 

Re: [Libvir] take 2 [Re: write(2) may write less than the total requested

2008-02-20 Thread Mark McLoughlin
On Wed, 2008-02-20 at 16:42 +0100, Jim Meyering wrote:

 If I do as you suggest and move the safewrite definition into util.h,
 and make it static inline, then both problems go away.

Make sure to try and build this with gcc-4.3.0 - remember that we
un-inlined xstrtol() because of:

  internal.h:272: error: inlining failed in call to 'xstrtol_i': call is 
unlikely and code size would grow

Again, the option is to stop using -Winline, but it's probably about
time we figured out some proper way of sharing code between libvirt.so
and virsh etc. without adding it to the ABI.

How about a libvirtutil libtool convenience (i.e. static) library?

Cheers,
Mark.

--
Libvir-list mailing list
Libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list


Re: [Libvir] take 2 [Re: write(2) may write less than the total requested

2008-02-20 Thread Jim Paris
Hi Jim, 

comments inline


Jim Meyering wrote:
 diff --git a/proxy/libvirt_proxy.c b/proxy/libvirt_proxy.c
 index d96d3db..a22ba6c 100644
 --- a/proxy/libvirt_proxy.c
 +++ b/proxy/libvirt_proxy.c
 @@ -2,7 +2,7 @@
   * proxy_svr.c: root suid proxy server for Xen access to APIs with no
   *  side effects from unauthenticated clients.
   *
 - * Copyright (C) 2006, 2007 Red Hat, Inc.
 + * Copyright (C) 2006, 2007, 2008 Red Hat, Inc.
   *
   * See COPYING.LIB for the License of this software
   *
 @@ -26,6 +26,7 @@
  #include internal.h
 
  #include proxy_internal.h
 +#include util.h
  #include xen_internal.h
  #include xend_internal.h
  #include xs_internal.h
 @@ -317,19 +318,12 @@ proxyWriteClientSocket(int nr, virProxyPacketPtr req) {
   return(-1);
  }
 
 -retry:
 -ret = write(pollInfos[nr].fd, (char *) req, req-len);
 +ret = safewrite(pollInfos[nr].fd, (char *) req, req-len);
  if (ret  0) {

Should this check (ret == req-len) instead?  safewrite() will return
an error if write() returns an error, regardless of how many bytes are
written, but it's still possible for it to return less than requested
if write() returns 0 (eof?).  The behavior of safewrite could be
adjusted to return errors in that case.


 -if (errno == EINTR) {
 - if (debug  0)
 - fprintf(stderr, write socket %d to client %d interrupted\n,
 - pollInfos[nr].fd, nr);
 - goto retry;
 - }
  fprintf(stderr, write %d bytes to socket %d from client %d 
 failed\n,
   req-len, pollInfos[nr].fd, nr);
 - proxyCloseClientSocket(nr);
 - return(-1);
 +proxyCloseClientSocket(nr);
 +return(-1);
  }
  if (ret == 0) {
   if (debug)
 diff --git a/qemud/qemud.c b/qemud/qemud.c
 index 3a5e44c..269e9fe 100644
 --- a/qemud/qemud.c
 +++ b/qemud/qemud.c
 @@ -118,7 +118,7 @@ static void sig_handler(int sig) {
  return;
 
  origerrno = errno;
 -r = write(sigwrite, sigc, 1);
 +r = safewrite(sigwrite, sigc, 1);

write() shouldn't do a partial write for size 1, but this is
necessary anyway to help with the EINTR case.  Might want to add that
benefit to the log message, it's not just short-writes this protects against.

  if (r == -1) {

if (r != 1)? 

  sig_errors++;
  sig_lasterrno = errno;
 @@ -1360,11 +1360,9 @@ static int qemudClientWriteBuf(struct qemud_server 
 *server,
 const char *data, int len) {
  int ret;
  if (!client-tlssession) {
 -if ((ret = write(client-fd, data, len)) == -1) {
 -if (errno != EAGAIN) {
 -qemudLog (QEMUD_ERR, _(write: %s), strerror (errno));
 -qemudDispatchClientFailure(server, client);
 -}
 +if ((ret = safewrite(client-fd, data, len)) == -1) {
 +qemudLog (QEMUD_ERR, _(write: %s), strerror (errno));
 +qemudDispatchClientFailure(server, client);

ret != len?

  return -1;
  }
  } else {
 diff --git a/src/conf.c b/src/conf.c
 index e0ecdea..53ea993 100644
 --- a/src/conf.c
 +++ b/src/conf.c
 @@ -904,7 +904,7 @@ __virConfWriteFile(const char *filename, virConfPtr conf)
   goto error;
  }
 
 -ret = write(fd, buf-content, buf-use);
 +ret = safewrite(fd, buf-content, buf-use);
  close(fd);
  if (ret != (int) buf-use) {
  virConfError(NULL, VIR_ERR_WRITE_FAILED, _(failed to save 
 content), 0);
 diff --git a/src/console.c b/src/console.c
 index 02a9c7f..1c6cba0 100644
 --- a/src/console.c
 +++ b/src/console.c
 @@ -1,7 +1,7 @@
  /*
   * console.c: A dumb serial console client
   *
 - * Copyright (C) 2007 Red Hat, Inc.
 + * Copyright (C) 2007, 2008 Red Hat, Inc.
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
 @@ -37,6 +37,7 @@
 
  #include console.h
  #include internal.h
 +#include util.h
 
  /* ie  Ctrl-]  as per telnet */
  #define CTRL_CLOSE_BRACKET '\35'
 @@ -161,7 +162,8 @@ int vshRunConsole(const char *tty) {
 
  while (sent  got) {
  int done;
 -if ((done = write(destfd, buf + sent, got - sent)) = 0) 
 {
 +if ((done = safewrite(destfd, buf + sent, got - sent))
 += 0) {

!= (got - sent)?

  fprintf(stderr, _(failure writing output: %s\n),
  strerror(errno));
  goto cleanup;
 diff --git a/src/proxy_internal.c b/src/proxy_internal.c
 index bc94763..c3e50c6 100644
 --- a/src/proxy_internal.c
 +++ b/src/proxy_internal.c
 @@ -1,7 +1,7 @@
  /*
   * proxy_client.c: client side of the communication with the libvirt proxy.
   *
 - * Copyright (C) 2006 Red Hat, Inc.
 + * Copyright (C) 2006, 2008 Red Hat, Inc.
   *
   * See COPYING.LIB for the License of this software
   *
 @@ -26,6 +26,7 @@
  #include internal.h
 

Re: [Libvir] take 2 [Re: write(2) may write less than the total requested

2008-02-20 Thread Jim Meyering
Mark McLoughlin [EMAIL PROTECTED] wrote:
 On Wed, 2008-02-20 at 16:42 +0100, Jim Meyering wrote:

 If I do as you suggest and move the safewrite definition into util.h,
 and make it static inline, then both problems go away.

   Make sure to try and build this with gcc-4.3.0 - remember that we
 un-inlined xstrtol() because of:

   internal.h:272: error: inlining failed in call to 'xstrtol_i': call is 
 unlikely and code size would grow

   Again, the option is to stop using -Winline, but it's probably about
 time we figured out some proper way of sharing code between libvirt.so
 and virsh etc. without adding it to the ABI.

   How about a libvirtutil libtool convenience (i.e. static) library?

This does sound like a better idea.
I'll prepare a patch to does that,
and probably move at least these into it, too:

__virStrToLong_i;
__virStrToLong_ull;

--
Libvir-list mailing list
Libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list


Re: [Libvir] take 2 [Re: write(2) may write less than the total requested

2008-02-20 Thread Jim Meyering
Jim Paris [EMAIL PROTECTED] wrote:
 Jim Meyering wrote:
...

Hi Jim,

 -retry:
 -ret = write(pollInfos[nr].fd, (char *) req, req-len);
 +ret = safewrite(pollInfos[nr].fd, (char *) req, req-len);
  if (ret  0) {

 Should this check (ret == req-len) instead?  safewrite() will return
 an error if write() returns an error, regardless of how many bytes are
 written,

It *could* perform that test, but I think it is slightly more
maintainable (no duplication of that potentially nontrivial expression)
and just as correct to check only ret  0.
That's why I made changes like this, too:

-if (write(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC)) != 
sizeof(TEST_SAVE_MAGIC)) {
+if (safewrite(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC))  0) {

Not only that, but the duplication removal makes it more readable because
the reader no longer has to visually ensure that the 3rd arg and the RHS
of the != comparison are the same.  As a bonus, that particular change
brings the line length below the 80-col threshold.

 but it's still possible for it to return less than requested
 if write() returns 0 (eof?).

Really?  How?  EOF is relevant to read, but not to write(2).

As I see it, calling safewrite can have only two outcomes:
  - return -1 to indicate failure
  - return the requested byte count (arg #3, count, which is non-negative)
The only way safewrite can return 0 is if its count argument is also 0,
and that's not a failure.  This is because write itself can return 0 only
if its count is also 0.

--
Libvir-list mailing list
Libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list


Re: [Libvir] take 2 [Re: write(2) may write less than the total requested

2008-02-20 Thread Jim Paris
Jim Meyering wrote:
 It *could* perform that test, but I think it is slightly more
 maintainable (no duplication of that potentially nontrivial expression)
 and just as correct to check only ret  0.

Not having the duplicated expression is certainly good, if it's
correct to do so (and it seems you're right).

  but it's still possible for it to return less than requested
  if write() returns 0 (eof?).
 
 Really?  How?  EOF is relevant to read, but not to write(2).
 
 As I see it, calling safewrite can have only two outcomes:
   - return -1 to indicate failure
   - return the requested byte count (arg #3, count, which is non-negative)
 The only way safewrite can return 0 is if its count argument is also 0,
 and that's not a failure.  This is because write itself can return 0 only
 if its count is also 0.

Hmm, I was thinking write might return 0 for closed pipes and similar
but you're right, that's different from EOF and should return error.

http://www.opengroup.org/onlinepubs/95399/functions/write.html
says:
  Where this volume of IEEE Std 1003.1-2001 requires -1 to be returned
  and errno set to [EAGAIN], most historical implementations return
  zero (with the O_NDELAY flag set, which is the historical
  predecessor of O_NONBLOCK, but is not itself in this volume of IEEE
  Std 1003.1-2001). The error indications in this volume of IEEE Std
  1003.1-2001 were chosen so that an application can distinguish these
  cases from end-of-file.
so we're safe here.

It does bring up the point that safewrite() doesn't handle EAGAIN and
might not be appropriate for non-blocking fds.  The sigwrite pipe is
non-blocking.  At quick glance, the qemud fds might be that way too?

It also makes me notice that we have 3 *SetNonBlock functions, two
with the same name even...

-jim


--
Libvir-list mailing list
Libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list