summaryrefslogtreecommitdiff
path: root/source4/lib
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2004-11-03 10:09:48 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:05:23 -0500
commitdde07058075d357cfdc63624c8dcaa67ebd40add (patch)
treec3f29090e37f1bc103a3d6051e708d1ebbe305a5 /source4/lib
parent90a8c4acc7e673e6439197776d19cc4b095ac322 (diff)
downloadsamba-dde07058075d357cfdc63624c8dcaa67ebd40add.tar.gz
samba-dde07058075d357cfdc63624c8dcaa67ebd40add.tar.bz2
samba-dde07058075d357cfdc63624c8dcaa67ebd40add.zip
r3507: - added deferred replies on sharing violation in pvfs open. The
deferred reply is short-circuited immediately when the file is closed by another user, allowing it to be opened by the waiting user. - added a sane set of timeval manipulation routines - converted all the events code and code that uses it to use struct timeval instead of time_t, which allows for microsecond resolution instead of 1 second resolution. This was needed for doing the pvfs deferred open code, and is why the patch is so big. (This used to be commit 0d51511d408d91eb5f68a35e980e0875299b1831)
Diffstat (limited to 'source4/lib')
-rw-r--r--source4/lib/basic.mk3
-rw-r--r--source4/lib/events.c23
-rw-r--r--source4/lib/messaging/messaging.c51
-rw-r--r--source4/lib/time.c141
-rw-r--r--source4/lib/unix_privs.c69
5 files changed, 258 insertions, 29 deletions
diff --git a/source4/lib/basic.mk b/source4/lib/basic.mk
index e7d710eec6..827aa0a57c 100644
--- a/source4/lib/basic.mk
+++ b/source4/lib/basic.mk
@@ -72,7 +72,8 @@ ADD_OBJ_FILES = \
lib/events.o \
lib/db_wrap.o \
lib/server_mutex.o \
- lib/idtree.o
+ lib/idtree.o \
+ lib/unix_privs.o
REQUIRED_SUBSYSTEMS = \
LIBTDB CHARSET LIBREPLACE LIBNETIF LIBCRYPTO
# End SUBSYSTEM LIBBASIC
diff --git a/source4/lib/events.c b/source4/lib/events.c
index f53a244c6d..0ca6b66598 100644
--- a/source4/lib/events.c
+++ b/source4/lib/events.c
@@ -285,15 +285,14 @@ void event_loop_exit(struct event_context *ev, int code)
*/
int event_loop_once(struct event_context *ev)
{
- time_t t;
fd_set r_fds, w_fds;
struct fd_event *fe;
struct loop_event *le;
struct timed_event *te;
int selrtn;
- struct timeval tval;
+ struct timeval tval, t;
- t = time(NULL);
+ t = timeval_current();
/* the loop events are called on each loop. Be careful to allow the
event to remove itself */
@@ -310,7 +309,6 @@ int event_loop_once(struct event_context *ev)
le = next;
}
- ZERO_STRUCT(tval);
FD_ZERO(&r_fds);
FD_ZERO(&w_fds);
@@ -336,17 +334,12 @@ int event_loop_once(struct event_context *ev)
/* start with a reasonable max timeout */
tval.tv_sec = 600;
+ tval.tv_usec = 0;
/* work out the right timeout for all timed events */
for (te=ev->timed_events;te;te=te->next) {
- int timeout = te->next_event - t;
- if (timeout < 0) {
- timeout = 0;
- }
- if (te->ref_count &&
- timeout < tval.tv_sec) {
- tval.tv_sec = timeout;
- }
+ struct timeval tv = timeval_diff(&te->next_event, &t);
+ tval = timeval_min(&tv, &tval);
}
/* only do a select() if there're fd_events
@@ -368,7 +361,7 @@ int event_loop_once(struct event_context *ev)
*/
selrtn = select(ev->maxfd+1, &r_fds, &w_fds, NULL, &tval);
- t = time(NULL);
+ t = timeval_current();
if (selrtn == -1 && errno == EBADF) {
/* the socket is dead! this should never
@@ -404,11 +397,11 @@ int event_loop_once(struct event_context *ev)
if (te->ref_count == 0) {
DLIST_REMOVE(ev->timed_events, te);
talloc_free(te);
- } else if (te->next_event <= t) {
+ } else if (timeval_compare(&te->next_event, &t) >= 0) {
te->ref_count++;
te->handler(ev, te, t);
te->ref_count--;
- if (te->next_event <= t) {
+ if (timeval_compare(&te->next_event, &t) >= 0) {
/* the handler didn't set a time for the
next event - remove the event */
event_remove_timed(ev, te);
diff --git a/source4/lib/messaging/messaging.c b/source4/lib/messaging/messaging.c
index f862b2d505..041554a7c0 100644
--- a/source4/lib/messaging/messaging.c
+++ b/source4/lib/messaging/messaging.c
@@ -29,6 +29,9 @@
/* change the message version with any incompatible changes in the protocol */
#define MESSAGING_VERSION 1
+/* the number of microseconds to backoff in retrying to send a message */
+#define MESSAGING_BACKOFF 250000
+
struct messaging_context {
servid_t server_id;
struct socket_context *sock;
@@ -119,7 +122,7 @@ static void messaging_dispatch(struct messaging_context *msg, struct messaging_r
handle IO for a single message
*/
static void messaging_recv_handler(struct event_context *ev, struct fd_event *fde,
- time_t t, uint16_t flags)
+ struct timeval t, uint16_t flags)
{
struct messaging_rec *rec = fde->private;
struct messaging_context *msg = rec->msg;
@@ -200,7 +203,7 @@ static int rec_destructor(void *ptr)
handle a new incoming connection
*/
static void messaging_listen_handler(struct event_context *ev, struct fd_event *fde,
- time_t t, uint16_t flags)
+ struct timeval t, uint16_t flags)
{
struct messaging_context *msg = fde->private;
struct messaging_rec *rec;
@@ -272,7 +275,7 @@ void messaging_deregister(struct messaging_context *msg, uint32_t msg_type, void
handle IO for sending a message
*/
static void messaging_send_handler(struct event_context *ev, struct fd_event *fde,
- time_t t, uint16_t flags)
+ struct timeval t, uint16_t flags)
{
struct messaging_rec *rec = fde->private;
NTSTATUS status;
@@ -324,19 +327,33 @@ static void messaging_send_handler(struct event_context *ev, struct fd_event *fd
/*
+ wrapper around socket_connect with raised privileges
+*/
+static NTSTATUS try_connect(struct messaging_rec *rec)
+{
+ NTSTATUS status;
+ void *priv = root_privileges();
+ status = socket_connect(rec->sock, NULL, 0, rec->path, 0, 0);
+ talloc_free(priv);
+ return status;
+}
+
+
+/*
when the servers listen queue is full we use this to backoff the message
*/
-static void messaging_backoff_handler(struct event_context *ev, struct timed_event *te, time_t t)
+static void messaging_backoff_handler(struct event_context *ev, struct timed_event *te,
+ struct timeval t)
{
struct messaging_rec *rec = te->private;
struct messaging_context *msg = rec->msg;
NTSTATUS status;
struct fd_event fde;
- status = socket_connect(rec->sock, NULL, 0, rec->path, 0, 0);
+ status = try_connect(rec);
if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
/* backoff again */
- te->next_event = t+1;
+ te->next_event = timeval_add(&t, 0, MESSAGING_BACKOFF);
return;
}
@@ -356,7 +373,7 @@ static void messaging_backoff_handler(struct event_context *ev, struct timed_eve
talloc_set_destructor(rec, rec_destructor);
- messaging_send_handler(msg->event.ev, rec->fde, 0, EVENT_FD_WRITE);
+ messaging_send_handler(msg->event.ev, rec->fde, timeval_zero(), EVENT_FD_WRITE);
}
@@ -396,11 +413,11 @@ NTSTATUS messaging_send(struct messaging_context *msg, servid_t server, uint32_t
rec->path = messaging_path(rec, server);
- status = socket_connect(rec->sock, NULL, 0, rec->path, 0, 0);
+ status = try_connect(rec);
if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
/* backoff on this message - the servers listen queue is full */
struct timed_event te;
- te.next_event = time(NULL)+1;
+ te.next_event = timeval_current_ofs(0, MESSAGING_BACKOFF);
te.handler = messaging_backoff_handler;
te.private = rec;
event_add_timed(msg->event.ev, &te);
@@ -421,11 +438,25 @@ NTSTATUS messaging_send(struct messaging_context *msg, servid_t server, uint32_t
talloc_set_destructor(rec, rec_destructor);
- messaging_send_handler(msg->event.ev, rec->fde, 0, EVENT_FD_WRITE);
+ messaging_send_handler(msg->event.ev, rec->fde, timeval_zero(), EVENT_FD_WRITE);
return NT_STATUS_OK;
}
+/*
+ Send a message to a particular server, with the message containing a single pointer
+*/
+NTSTATUS messaging_send_ptr(struct messaging_context *msg, servid_t server,
+ uint32_t msg_type, void *ptr)
+{
+ DATA_BLOB blob;
+
+ blob.data = (void *)&ptr;
+ blob.length = sizeof(void *);
+
+ return messaging_send(msg, server, msg_type, &blob);
+}
+
/*
destroy the messaging context
diff --git a/source4/lib/time.c b/source4/lib/time.c
index cfceebf9cb..9e6da2cfa6 100644
--- a/source4/lib/time.c
+++ b/source4/lib/time.c
@@ -386,9 +386,144 @@ NTTIME nttime_from_string(const char *s)
return strtoull(s, NULL, 0);
}
-int64_t usec_time_diff(struct timeval *larget, struct timeval *smallt)
+/*
+ return (tv1 - tv2) in microseconds
+*/
+int64_t usec_time_diff(struct timeval *tv1, struct timeval *tv2)
+{
+ int64_t sec_diff = tv1->tv_sec - tv2->tv_sec;
+ return (sec_diff * 1000000) + (int64_t)(tv1->tv_usec - tv2->tv_usec);
+}
+
+
+/*
+ return a zero timeval
+*/
+struct timeval timeval_zero(void)
{
- int64_t sec_diff = larget->tv_sec - smallt->tv_sec;
- return (sec_diff * 1000000) + (int64_t)(larget->tv_usec - smallt->tv_usec);
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ return tv;
}
+/*
+ return a timeval for the current time
+*/
+struct timeval timeval_current(void)
+{
+ struct timeval tv;
+ GetTimeOfDay(&tv);
+ return tv;
+}
+
+/*
+ return a timeval struct with the given elements
+*/
+struct timeval timeval_set(uint32_t secs, uint32_t usecs)
+{
+ struct timeval tv;
+ tv.tv_sec = secs;
+ tv.tv_usec = usecs;
+ return tv;
+}
+
+
+/*
+ return a timeval ofs microseconds after tv
+*/
+struct timeval timeval_add(struct timeval *tv, uint32_t secs, uint32_t usecs)
+{
+ struct timeval tv2 = *tv;
+ const uint_t million = 1000000;
+ tv2.tv_sec += secs;
+ tv2.tv_usec += usecs;
+ tv2.tv_sec += tv2.tv_usec / million;
+ tv2.tv_usec = tv2.tv_usec % million;
+ return tv2;
+}
+
+/*
+ return the sum of two timeval structures
+*/
+struct timeval timeval_sum(struct timeval *tv1, struct timeval *tv2)
+{
+ return timeval_add(tv1, tv2->tv_sec, tv2->tv_usec);
+}
+
+/*
+ return a timeval secs/usecs into the future
+*/
+struct timeval timeval_current_ofs(uint32_t secs, uint32_t usecs)
+{
+ struct timeval tv = timeval_current();
+ return timeval_add(&tv, secs, usecs);
+}
+
+/*
+ compare two timeval structures.
+ Return 1 if tv2 > tv1
+ Return 0 if tv2 == tv1
+ Return -1 if tv2 < tv1
+*/
+int timeval_compare(struct timeval *tv1, struct timeval *tv2)
+{
+ if (tv2->tv_sec > tv1->tv_sec) return 1;
+ if (tv2->tv_sec < tv1->tv_sec) return -1;
+ if (tv2->tv_usec > tv1->tv_usec) return 1;
+ if (tv2->tv_usec < tv1->tv_usec) return -1;
+ return 0;
+}
+
+/*
+ return True if a timer is in the past
+*/
+BOOL timeval_expired(struct timeval *tv)
+{
+ struct timeval tv2 = timeval_current();
+ if (tv2.tv_sec > tv->tv_sec) return True;
+ if (tv2.tv_sec < tv->tv_sec) return False;
+ return (tv2.tv_usec >= tv->tv_usec);
+}
+
+/*
+ return the number of seconds elapsed since a given time
+*/
+double timeval_elapsed(struct timeval *tv)
+{
+ struct timeval tv2 = timeval_current();
+ return (tv2.tv_sec - tv->tv_sec) +
+ (tv2.tv_usec - tv->tv_usec)*1.0e-6;
+}
+
+/*
+ return the lesser of two timevals
+*/
+struct timeval timeval_min(struct timeval *tv1, struct timeval *tv2)
+{
+ if (tv1->tv_sec < tv2->tv_sec) return *tv1;
+ if (tv1->tv_sec > tv2->tv_sec) return *tv2;
+ if (tv1->tv_usec < tv2->tv_usec) return *tv1;
+ return *tv2;
+}
+
+/*
+ return the difference between two timevals as a timeval
+ if tv2 comes after tv1, then return a zero timeval
+ (this is *tv1 - *tv2)
+*/
+struct timeval timeval_diff(struct timeval *tv1, struct timeval *tv2)
+{
+ struct timeval t;
+ if (timeval_compare(tv1, tv2) >= 0) {
+ return timeval_zero();
+ }
+ t.tv_sec = tv1->tv_sec - tv2->tv_sec;
+ if (tv2->tv_usec > tv1->tv_usec) {
+ t.tv_sec--;
+ t.tv_usec = 1000000 - (tv2->tv_usec - tv1->tv_usec);
+ } else {
+ t.tv_usec = tv1->tv_usec - tv2->tv_usec;
+ }
+ return t;
+}
diff --git a/source4/lib/unix_privs.c b/source4/lib/unix_privs.c
new file mode 100644
index 0000000000..c65f490aeb
--- /dev/null
+++ b/source4/lib/unix_privs.c
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ gain/lose root privileges
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "system/passwd.h"
+
+/*
+ there are times when smbd needs to temporarily gain root privileges
+ to do some operation. To do this you call root_privileges(), which
+ returns a talloc handle. To restore your previous privileges
+ talloc_free() this pointer.
+
+ Note that this call is considered successful even if it does not
+ manage to gain too privileges, but it will call smb_abort() if it
+ fails to restore the privileges afterwards. The logic is that
+ failing to gain root access can be caught by whatever operation
+ needs to be run as root failing, but failing to lose the root
+ privileges is dangerous.
+
+ This also means that this code is safe to be called from completely
+ unprivileged processes.
+*/
+
+struct saved_state {
+ uid_t uid;
+};
+
+static int privileges_destructor(void *ptr)
+{
+ struct saved_state *s = ptr;
+ if (geteuid() != s->uid &&
+ seteuid(s->uid) != 0) {
+ smb_panic("Failed to restore privileges");
+ }
+ return 0;
+}
+
+void *root_privileges(void)
+{
+ struct saved_state *s;
+ s = talloc_p(NULL, struct saved_state);
+ if (!s) return NULL;
+ s->uid = geteuid();
+ if (s->uid != 0) {
+ seteuid(0);
+ }
+ talloc_set_destructor(s, privileges_destructor);
+ return s;
+}
+