From 61c58824cc9117ffe206ae7c126929bfa2384486 Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss@samba.org>
Date: Thu, 10 Jul 2014 00:18:10 +0200
Subject: [PATCH 1/7] PATCHSET18: printing: traverse_read the printer list for
 share updates
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The printcap update procedure involves the background printer process
obtaining the printcap information from the printing backend, writing
this to printer_list.tdb, and then notifying all smbd processes of the
new list. The processes then all attempt to simultaneously traverse
printer_list.tdb, in order to update their local share lists.

With a large number of printers, and a large number of per-client smbd
processes, this traversal results in significant lock contention, mostly
due to the fact that the traversal is unnecessarily done with an
exclusive (write) lock on the printer_list.tdb database.

This commit changes the share update code path to perform a read-only
traversal.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=10652

Reported-by: Alex K <korobkin+samba@gmail.com>
Reported-by: Franz Pförtsch <franz.pfoertsch@brose.com>
Signed-off-by: David Disseldorp <ddiss@samba.org>
---
 source3/printing/load.c         |  2 +-
 source3/printing/pcap.c         |  4 ++--
 source3/printing/pcap.h         |  2 +-
 source3/printing/printer_list.c | 17 +++++++++++------
 source3/printing/printer_list.h |  4 ++--
 5 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/source3/printing/load.c b/source3/printing/load.c
index 829c3e3..0a3de73 100644
--- a/source3/printing/load.c
+++ b/source3/printing/load.c
@@ -70,5 +70,5 @@ void load_printers(struct tevent_context *ev,
 
 	/* load all printcap printers */
 	if (lp_load_printers() && lp_servicenumber(PRINTERS_NAME) >= 0)
-		pcap_printer_fn(lp_add_one_printer, NULL);
+		pcap_printer_read_fn(lp_add_one_printer, NULL);
 }
diff --git a/source3/printing/pcap.c b/source3/printing/pcap.c
index 62db4f5..6ad8e33 100644
--- a/source3/printing/pcap.c
+++ b/source3/printing/pcap.c
@@ -229,11 +229,11 @@ void pcap_printer_fn_specific(const struct pcap_cache *pc,
 	return;
 }
 
-void pcap_printer_fn(void (*fn)(const char *, const char *, const char *, void *), void *pdata)
+void pcap_printer_read_fn(void (*fn)(const char *, const char *, const char *, void *), void *pdata)
 {
 	NTSTATUS status;
 
-	status = printer_list_run_fn(fn, pdata);
+	status = printer_list_read_run_fn(fn, pdata);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(3, ("Failed to run fn for all printers!\n"));
 	}
diff --git a/source3/printing/pcap.h b/source3/printing/pcap.h
index 7056213..6c062c3 100644
--- a/source3/printing/pcap.h
+++ b/source3/printing/pcap.h
@@ -39,7 +39,7 @@ bool pcap_cache_add(const char *name, const char *comment, const char *location)
 bool pcap_cache_loaded(void);
 bool pcap_cache_replace(const struct pcap_cache *cache);
 void pcap_printer_fn_specific(const struct pcap_cache *, void (*fn)(const char *, const char *, const char *, void *), void *);
-void pcap_printer_fn(void (*fn)(const char *, const char *, const char *, void *), void *);
+void pcap_printer_read_fn(void (*fn)(const char *, const char *, const char *, void *), void *);
 
 void pcap_cache_reload(struct tevent_context *ev,
 		       struct messaging_context *msg_ctx,
diff --git a/source3/printing/printer_list.c b/source3/printing/printer_list.c
index 603ce4b..b24bf83 100644
--- a/source3/printing/printer_list.c
+++ b/source3/printing/printer_list.c
@@ -280,7 +280,8 @@ done:
 typedef int (printer_list_trv_fn_t)(struct db_record *, void *);
 
 static NTSTATUS printer_list_traverse(printer_list_trv_fn_t *fn,
-						void *private_data)
+				      void *private_data,
+				      bool read_only)
 {
 	struct db_context *db;
 	int ret;
@@ -290,7 +291,11 @@ static NTSTATUS printer_list_traverse(printer_list_trv_fn_t *fn,
 		return NT_STATUS_INTERNAL_DB_CORRUPTION;
 	}
 
-	ret = db->traverse(db, fn, private_data);
+	if (read_only) {
+		ret = db->traverse_read(db, fn, private_data);
+	} else {
+		ret = db->traverse(db, fn, private_data);
+	}
 	if (ret < 0) {
 		return NT_STATUS_UNSUCCESSFUL;
 	}
@@ -357,7 +362,7 @@ NTSTATUS printer_list_clean_old(void)
 
 	state.status = NT_STATUS_OK;
 
-	status = printer_list_traverse(printer_list_clean_fn, &state);
+	status = printer_list_traverse(printer_list_clean_fn, &state, false);
 	if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) &&
 	    !NT_STATUS_IS_OK(state.status)) {
 		status = state.status;
@@ -404,8 +409,8 @@ static int printer_list_exec_fn(struct db_record *rec, void *private_data)
 	return 0;
 }
 
-NTSTATUS printer_list_run_fn(void (*fn)(const char *, const char *, const char *, void *),
-			     void *private_data)
+NTSTATUS printer_list_read_run_fn(void (*fn)(const char *, const char *, const char *, void *),
+				  void *private_data)
 {
 	struct printer_list_exec_state state;
 	NTSTATUS status;
@@ -414,7 +419,7 @@ NTSTATUS printer_list_run_fn(void (*fn)(const char *, const char *, const char *
 	state.private_data = private_data;
 	state.status = NT_STATUS_OK;
 
-	status = printer_list_traverse(printer_list_exec_fn, &state);
+	status = printer_list_traverse(printer_list_exec_fn, &state, true);
 	if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) &&
 	    !NT_STATUS_IS_OK(state.status)) {
 		status = state.status;
diff --git a/source3/printing/printer_list.h b/source3/printing/printer_list.h
index fb2e007..b12c192 100644
--- a/source3/printing/printer_list.h
+++ b/source3/printing/printer_list.h
@@ -100,6 +100,6 @@ NTSTATUS printer_list_mark_reload(void);
  */
 NTSTATUS printer_list_clean_old(void);
 
-NTSTATUS printer_list_run_fn(void (*fn)(const char *, const char *, const char *, void *),
-			     void *private_data);
+NTSTATUS printer_list_read_run_fn(void (*fn)(const char *, const char *, const char *, void *),
+				  void *private_data);
 #endif /* _PRINTER_LIST_H_ */
-- 
2.1.0


From 18b15f127b656ad9232789b073460c95b1aaa835 Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss@samba.org>
Date: Fri, 11 Jul 2014 17:00:05 +0200
Subject: [PATCH 2/7] PATCHSET18: printing: only reload printer shares on
 client enum

Currently, automatic printer share updates are handled in the following
way:
- Background printer process (BPP) forked on startup
- Parent smbd and per-client children await MSG_PRINTER_PCAP messages
- BPP periodically polls the printing backend for printcap data
	- printcap data written to printer_list.tdb
	- MSG_PRINTER_PCAP sent to all smbd processes following update
- smbd processes all read the latest printer_list.tdb data, and update
  their share listings

This procedure is not scalable, as all smbd processes hit
printer_list.tdb in parallel, resulting in a large spike in CPU usage.

This change sees smbd processes only update their printer share lists
only when a client asks for this information, e.g. via NetShareEnum or
EnumPrinters.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=10652

Suggested-by: Volker Lendecke <vl@samba.org>
Signed-off-by: David Disseldorp <ddiss@samba.org>
---
 source3/printing/spoolssd.c                 | 17 +----------------
 source3/rpc_server/spoolss/srv_spoolss_nt.c | 11 ++++++++++-
 source3/rpc_server/srvsvc/srv_srvsvc_nt.c   |  1 +
 source3/smbd/lanman.c                       |  3 +++
 source3/smbd/server.c                       | 27 +++++----------------------
 5 files changed, 20 insertions(+), 39 deletions(-)

diff --git a/source3/printing/spoolssd.c b/source3/printing/spoolssd.c
index 83727df..7953237 100644
--- a/source3/printing/spoolssd.c
+++ b/source3/printing/spoolssd.c
@@ -74,20 +74,6 @@ static void smb_conf_updated(struct messaging_context *msg,
 	spoolss_reopen_logs();
 }
 
-static void spoolss_pcap_updated(struct messaging_context *msg,
-				 void *private_data,
-				 uint32_t msg_type,
-				 struct server_id server_id,
-				 DATA_BLOB *data)
-{
-	struct tevent_context *ev_ctx = talloc_get_type_abort(private_data,
-							     struct tevent_context);
-
-	DEBUG(10, ("Got message saying pcap was updated. Reloading.\n"));
-	change_to_root_user();
-	reload_printers(ev_ctx, msg);
-}
-
 static void spoolss_sig_term_handler(struct tevent_context *ev,
 				     struct tevent_signal *se,
 				     int signum,
@@ -206,12 +192,11 @@ void start_spoolssd(struct tevent_context *ev_ctx,
 		exit(1);
 	}
 
+	/* printer shares updated from printer_list.tdb on client enumeration */
 	messaging_register(msg_ctx, NULL,
 			   MSG_PRINTER_UPDATE, print_queue_receive);
 	messaging_register(msg_ctx, ev_ctx,
 			   MSG_SMB_CONF_UPDATED, smb_conf_updated);
-	messaging_register(msg_ctx, ev_ctx,
-			   MSG_PRINTER_PCAP, spoolss_pcap_updated);
 
 	/*
 	 * Initialize spoolss with an init function to convert printers first.
diff --git a/source3/rpc_server/spoolss/srv_spoolss_nt.c b/source3/rpc_server/spoolss/srv_spoolss_nt.c
index 516b7dc..db48574 100644
--- a/source3/rpc_server/spoolss/srv_spoolss_nt.c
+++ b/source3/rpc_server/spoolss/srv_spoolss_nt.c
@@ -4316,12 +4316,21 @@ static WERROR enum_all_printers_info_level(TALLOC_CTX *mem_ctx,
 					   uint32_t *count_p)
 {
 	int snum;
-	int n_services = lp_numservices();
+	int n_services;
 	union spoolss_PrinterInfo *info = NULL;
 	uint32_t count = 0;
 	WERROR result = WERR_OK;
 	struct dcerpc_binding_handle *b = NULL;
 
+	/*
+	 * printer shares are only updated on client enumeration. The background
+	 * printer process updates printer_list.tdb at regular intervals.
+	 */
+	become_root();
+	reload_printers(messaging_event_context(msg_ctx), msg_ctx);
+	unbecome_root();
+
+	n_services = lp_numservices();
 	*count_p = 0;
 	*info_p = NULL;
 
diff --git a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
index b9345d6..4600da3 100644
--- a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
+++ b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
@@ -568,6 +568,7 @@ static WERROR init_srv_share_info_ctr(struct pipes_struct *p,
 
 	/* Ensure all the usershares are loaded. */
 	become_root();
+	reload_printers(messaging_event_context(p->msg_ctx), p->msg_ctx);
 	load_usershare_shares();
 	load_registry_shares();
 	num_services = lp_numservices();
diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c
index f56ea30..49f7583 100644
--- a/source3/smbd/lanman.c
+++ b/source3/smbd/lanman.c
@@ -43,6 +43,7 @@
 #include "passdb/machine_sid.h"
 #include "auth.h"
 #include "rpc_server/rpc_ncacn_np.h"
+#include "messages.h"
 
 #ifdef CHECK_TYPES
 #undef CHECK_TYPES
@@ -2091,6 +2092,8 @@ static bool api_RNetShareEnum(struct smbd_server_connection *sconn,
 
 	/* Ensure all the usershares are loaded. */
 	become_root();
+	reload_printers(messaging_event_context(sconn->msg_ctx),
+			sconn->msg_ctx);
 	load_registry_shares();
 	count = load_usershare_shares();
 	unbecome_root();
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index a26dbc4..102e8dd 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -111,24 +111,6 @@ static void smb_conf_updated(struct messaging_context *msg,
 	/* printer reload triggered by background printing process */
 }
 
-/*******************************************************************
- What to do when printcap is updated.
- ********************************************************************/
-
-static void smb_pcap_updated(struct messaging_context *msg,
-			     void *private_data,
-			     uint32_t msg_type,
-			     struct server_id server_id,
-			     DATA_BLOB *data)
-{
-	struct tevent_context *ev_ctx =
-		talloc_get_type_abort(private_data, struct tevent_context);
-
-	DEBUG(10,("Got message saying pcap was updated. Reloading.\n"));
-	change_to_root_user();
-	reload_printers(ev_ctx, msg);
-}
-
 static void smbd_sig_term_handler(struct tevent_context *ev,
 				  struct tevent_signal *se,
 				  int signum,
@@ -1287,10 +1269,11 @@ extern void build_options(bool screen);
 
 	if (is_daemon && !interactive
 	    && lp_parm_bool(-1, "smbd", "backgroundqueue", true)) {
-		/* background queue is responsible for printcap cache updates */
-		messaging_register(smbd_server_conn->msg_ctx,
-				   smbd_event_context(),
-				   MSG_PRINTER_PCAP, smb_pcap_updated);
+		/*
+		 * background queue is responsible for printcap cache updates.
+		 * Other smbd processes only reload printers when a client
+		 * issues an enumeration request.
+		 */
 		start_background_queue(server_event_context(),
 				       smbd_server_conn->msg_ctx);
 	} else {
-- 
2.1.0


From 52196380547dde4784e42c35c46135bb5230a08d Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss@samba.org>
Date: Tue, 22 Jul 2014 20:17:38 +0200
Subject: [PATCH 3/7] PATCHSET18: printing: reload printer_list.tdb from in
 memory list

This will allow in future for a single atomic printer_list.tdb update.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=10652

Signed-off-by: David Disseldorp <ddiss@samba.org>
---
 source3/printing/pcap.c           | 26 +++++++++++---------------
 source3/printing/pcap.h           |  8 ++++----
 source3/printing/print_aix.c      | 17 ++++++++++++++---
 source3/printing/print_iprint.c   | 16 ++++++++++------
 source3/printing/print_standard.c |  8 ++++++--
 source3/printing/print_svid.c     | 11 +++++++----
 6 files changed, 52 insertions(+), 34 deletions(-)

diff --git a/source3/printing/pcap.c b/source3/printing/pcap.c
index 6ad8e33..5173fc9 100644
--- a/source3/printing/pcap.c
+++ b/source3/printing/pcap.c
@@ -83,7 +83,7 @@ void pcap_cache_destroy_specific(struct pcap_cache **pp_cache)
 	*pp_cache = NULL;
 }
 
-bool pcap_cache_add(const char *name, const char *comment, const char *location)
+static bool pcap_cache_add(const char *name, const char *comment, const char *location)
 {
 	NTSTATUS status;
 	time_t t = time_mono(NULL);
@@ -132,8 +132,8 @@ void pcap_cache_reload(struct tevent_context *ev,
 {
 	const char *pcap_name = lp_printcapname();
 	bool pcap_reloaded = False;
-	NTSTATUS status;
 	bool post_cache_fill_fn_handled = false;
+	struct pcap_cache *pcache = NULL;
 
 	DEBUG(3, ("reloading printcap cache\n"));
 
@@ -143,12 +143,6 @@ void pcap_cache_reload(struct tevent_context *ev,
 		return;
 	}
 
-	status = printer_list_mark_reload();
-	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(0, ("Failed to mark printer list for reload!\n"));
-		return;
-	}
-
 #ifdef HAVE_CUPS
 	if (strequal(pcap_name, "cups")) {
 		pcap_reloaded = cups_cache_reload(ev, msg_ctx,
@@ -164,26 +158,26 @@ void pcap_cache_reload(struct tevent_context *ev,
 
 #ifdef HAVE_IPRINT
 	if (strequal(pcap_name, "iprint")) {
-		pcap_reloaded = iprint_cache_reload();
+		pcap_reloaded = iprint_cache_reload(&pcache);
 		goto done;
 	}
 #endif
 
 #if defined(SYSV) || defined(HPUX)
 	if (strequal(pcap_name, "lpstat")) {
-		pcap_reloaded = sysv_cache_reload();
+		pcap_reloaded = sysv_cache_reload(&pcache);
 		goto done;
 	}
 #endif
 
 #ifdef AIX
 	if (strstr_m(pcap_name, "/qconfig") != NULL) {
-		pcap_reloaded = aix_cache_reload();
+		pcap_reloaded = aix_cache_reload(&pcache);
 		goto done;
 	}
 #endif
 
-	pcap_reloaded = std_pcap_cache_reload(pcap_name);
+	pcap_reloaded = std_pcap_cache_reload(pcap_name, &pcache);
 
 done:
 	DEBUG(3, ("reload status: %s\n", (pcap_reloaded) ? "ok" : "error"));
@@ -192,14 +186,16 @@ done:
 		/* cleanup old entries only if the operation was successful,
 		 * otherwise keep around the old entries until we can
 		 * successfuly reaload */
-		status = printer_list_clean_old();
-		if (!NT_STATUS_IS_OK(status)) {
-			DEBUG(0, ("Failed to cleanup printer list!\n"));
+
+		if (!pcap_cache_replace(pcache)) {
+			DEBUG(0, ("Failed to replace printer list!\n"));
 		}
+
 		if (post_cache_fill_fn != NULL) {
 			post_cache_fill_fn(ev, msg_ctx);
 		}
 	}
+	pcap_cache_destroy_specific(&pcache);
 
 	return;
 }
diff --git a/source3/printing/pcap.h b/source3/printing/pcap.h
index 6c062c3..d388d7d 100644
--- a/source3/printing/pcap.h
+++ b/source3/printing/pcap.h
@@ -49,7 +49,7 @@ bool pcap_printername_ok(const char *printername);
 
 /* The following definitions come from printing/print_aix.c  */
 
-bool aix_cache_reload(void);
+bool aix_cache_reload(struct pcap_cache **_pcache);
 
 /* The following definitions come from printing/print_cups.c  */
 
@@ -60,13 +60,13 @@ bool cups_cache_reload(struct tevent_context *ev,
 
 /* The following definitions come from printing/print_iprint.c  */
 
-bool iprint_cache_reload(void);
+bool iprint_cache_reload(struct pcap_cache **_pcache);
 
 /* The following definitions come from printing/print_svid.c  */
 
-bool sysv_cache_reload(void);
+bool sysv_cache_reload(struct pcap_cache **_pcache);
 
 /* The following definitions come from printing/print_standard.c  */
-bool std_pcap_cache_reload(const char *pcap_name);
+bool std_pcap_cache_reload(const char *pcap_name, struct pcap_cache **_pcache);
 
 #endif /* _PRINTING_PCAP_H_ */
diff --git a/source3/printing/print_aix.c b/source3/printing/print_aix.c
index 23d9a86..927a71b 100644
--- a/source3/printing/print_aix.c
+++ b/source3/printing/print_aix.c
@@ -29,12 +29,13 @@
 #include "printing/pcap.h"
 
 #ifdef AIX
-bool aix_cache_reload(void)
+bool aix_cache_reload(struct pcap_cache **_pcache)
 {
 	int iEtat;
 	XFILE *pfile;
 	char *line = NULL, *p;
 	char *name = NULL;
+	struct pcap_cache *pcache = NULL;
 	TALLOC_CTX *ctx = talloc_init("aix_cache_reload");
 
 	if (!ctx) {
@@ -52,6 +53,8 @@ bool aix_cache_reload(void)
 	iEtat = 0;
 	/* scan qconfig file for searching <printername>:	*/
 	for (;(line = fgets_slash(NULL, 1024, pfile)); free(line)) {
+		bool ok;
+
 		if (*line == '*' || *line == 0)
 			continue;
 
@@ -67,6 +70,7 @@ bool aix_cache_reload(void)
 				if (strcmp(p, "bsh") != 0) {
 					name = talloc_strdup(ctx, p);
 					if (!name) {
+						pcap_cache_destroy_specific(&pcache);
 						SAFE_FREE(line);
 						x_fclose(pfile);
 						TALLOC_FREE(ctx);
@@ -86,7 +90,10 @@ bool aix_cache_reload(void)
 				/* name is found without stanza device  */
 				/* probably a good printer ???		*/
 				iEtat = 0;
-				if (!pcap_cache_add(name, NULL, NULL)) {
+				ok = pcap_cache_add_specific(&pcache,
+							     name, NULL, NULL);
+				if (!ok) {
+					pcap_cache_destroy_specific(&pcache);
 					SAFE_FREE(line);
 					x_fclose(pfile);
 					TALLOC_FREE(ctx);
@@ -101,7 +108,10 @@ bool aix_cache_reload(void)
 			} else if (strstr_m(line, "device")) {
 				/* it's a good virtual printer */
 				iEtat = 0;
-				if (!pcap_cache_add(name, NULL, NULL)) {
+				ok = pcap_cache_add_specific(&pcache,
+							     name, NULL, NULL);
+				if (!ok) {
+					pcap_cache_destroy_specific(&pcache);
 					SAFE_FREE(line);
 					x_fclose(pfile);
 					TALLOC_FREE(ctx);
@@ -113,6 +123,7 @@ bool aix_cache_reload(void)
 		}
 	}
 
+	*_pcache = pcache;
 	x_fclose(pfile);
 	TALLOC_FREE(ctx);
 	return true;
diff --git a/source3/printing/print_iprint.c b/source3/printing/print_iprint.c
index 529f0dd..6e91747 100644
--- a/source3/printing/print_iprint.c
+++ b/source3/printing/print_iprint.c
@@ -204,7 +204,8 @@ static int iprint_get_server_version(http_t *http, char* serviceUri)
 
 static int iprint_cache_add_printer(http_t *http,
 				   int reqId,
-				   char* url)
+				   char *url,
+				   struct pcap_cache **pcache)
 {
 	ipp_t		*request = NULL,	/* IPP Request */
 			*response = NULL;	/* IPP Response */
@@ -340,7 +341,7 @@ static int iprint_cache_add_printer(http_t *http,
 		*/
 
 		if (name != NULL && !secure && smb_enabled) 
-			pcap_cache_add(name, info, NULL);
+			pcap_cache_add_specific(pcache, name, info, NULL);
 	}
 
  out:
@@ -349,7 +350,7 @@ static int iprint_cache_add_printer(http_t *http,
 	return(0);
 }
 
-bool iprint_cache_reload(void)
+bool iprint_cache_reload(struct pcap_cache **_pcache)
 {
 	http_t		*http = NULL;		/* HTTP connection to server */
 	ipp_t		*request = NULL,	/* IPP Request */
@@ -357,7 +358,8 @@ bool iprint_cache_reload(void)
 	ipp_attribute_t	*attr;			/* Current attribute */
 	cups_lang_t	*language = NULL;	/* Default language */
 	int		i;
-	bool ret = False;
+	bool ret = false;
+	struct pcap_cache *pcache = NULL;
 
 	DEBUG(5, ("reloading iprint printcap cache\n"));
 
@@ -439,14 +441,16 @@ bool iprint_cache_reload(void)
 					char *url = ippGetString(attr, i, NULL);
 					if (!url || !strlen(url))
 						continue;
-					iprint_cache_add_printer(http, i+2, url);
+					iprint_cache_add_printer(http, i+2, url,
+								 &pcache);
 				}
 			}
 			attr = ippNextAttribute(response);
 		}
 	}
 
-	ret = True;
+	ret = true;
+	*_pcache = pcache;
 
  out:
 	if (response)
diff --git a/source3/printing/print_standard.c b/source3/printing/print_standard.c
index c4f9c5b..b5f1056 100644
--- a/source3/printing/print_standard.c
+++ b/source3/printing/print_standard.c
@@ -59,10 +59,11 @@
 #include "printing/pcap.h"
 
 /* handle standard printcap - moved from pcap_printer_fn() */
-bool std_pcap_cache_reload(const char *pcap_name)
+bool std_pcap_cache_reload(const char *pcap_name, struct pcap_cache **_pcache)
 {
 	XFILE *pcap_file;
 	char *pcap_line;
+	struct pcap_cache *pcache = NULL;
 
 	if ((pcap_file = x_fopen(pcap_name, O_RDONLY, 0)) == NULL) {
 		DEBUG(0, ("Unable to open printcap file %s for read!\n", pcap_name));
@@ -117,12 +118,15 @@ bool std_pcap_cache_reload(const char *pcap_name)
 			}
 		}
 
-		if (*name && !pcap_cache_add(name, comment, NULL)) {
+		if ((*name != '\0')
+		 && !pcap_cache_add_specific(&pcache, name, comment, NULL)) {
 			x_fclose(pcap_file);
+			pcap_cache_destroy_specific(&pcache);
 			return false;
 		}
 	}
 
 	x_fclose(pcap_file);
+	*_pcache = pcache;
 	return true;
 }
diff --git a/source3/printing/print_svid.c b/source3/printing/print_svid.c
index 2226493..879661b 100644
--- a/source3/printing/print_svid.c
+++ b/source3/printing/print_svid.c
@@ -35,10 +35,11 @@
 #include "printing/pcap.h"
 
 #if defined(SYSV) || defined(HPUX)
-bool sysv_cache_reload(void)
+bool sysv_cache_reload(struct pcap_cache **_pcache)
 {
 	char **lines;
 	int i;
+	struct pcap_cache *pcache = NULL;
 
 #if defined(HPUX)
 	DEBUG(5, ("reloading hpux printcap cache\n"));
@@ -111,14 +112,16 @@ bool sysv_cache_reload(void)
 			*tmp = '\0';
 		
 		/* add it to the cache */
-		if (!pcap_cache_add(name, NULL, NULL)) {
+		if (!pcap_cache_add_specific(&pcache, name, NULL, NULL)) {
 			TALLOC_FREE(lines);
-			return False;
+			pcap_cache_destroy_specific(&pcache);
+			return false;
 		}
 	}
 
 	TALLOC_FREE(lines);
-	return True;
+	*_pcache = pcache;
+	return true;
 }
 
 #else
-- 
2.1.0


From 91c0b6477fcd4ad20d1cda45f78f160cee8e58ff Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss@samba.org>
Date: Fri, 25 Jul 2014 12:18:54 +0200
Subject: [PATCH 4/7] PATCHSET18: printing: remove pcap_cache_add()

All print list updates are now done via pcap_cache_replace(), which can
call into the print_list code directly.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=10652

Signed-off-by: David Disseldorp <ddiss@samba.org>
---
 source3/printing/pcap.c | 16 ++++++----------
 source3/printing/pcap.h |  1 -
 2 files changed, 6 insertions(+), 11 deletions(-)

diff --git a/source3/printing/pcap.c b/source3/printing/pcap.c
index 5173fc9..5059f20 100644
--- a/source3/printing/pcap.c
+++ b/source3/printing/pcap.c
@@ -83,15 +83,6 @@ void pcap_cache_destroy_specific(struct pcap_cache **pp_cache)
 	*pp_cache = NULL;
 }
 
-static bool pcap_cache_add(const char *name, const char *comment, const char *location)
-{
-	NTSTATUS status;
-	time_t t = time_mono(NULL);
-
-	status = printer_list_set_printer(talloc_tos(), name, comment, location, t);
-	return NT_STATUS_IS_OK(status);
-}
-
 bool pcap_cache_loaded(void)
 {
 	NTSTATUS status;
@@ -105,6 +96,7 @@ bool pcap_cache_replace(const struct pcap_cache *pcache)
 {
 	const struct pcap_cache *p;
 	NTSTATUS status;
+	time_t t = time_mono(NULL);
 
 	status = printer_list_mark_reload();
 	if (!NT_STATUS_IS_OK(status)) {
@@ -113,7 +105,11 @@ bool pcap_cache_replace(const struct pcap_cache *pcache)
 	}
 
 	for (p = pcache; p; p = p->next) {
-		pcap_cache_add(p->name, p->comment, p->location);
+		status = printer_list_set_printer(talloc_tos(), p->name,
+						  p->comment, p->location, t);
+		if (!NT_STATUS_IS_OK(status)) {
+			return false;
+		}
 	}
 
 	status = printer_list_clean_old();
diff --git a/source3/printing/pcap.h b/source3/printing/pcap.h
index d388d7d..7dccf84 100644
--- a/source3/printing/pcap.h
+++ b/source3/printing/pcap.h
@@ -35,7 +35,6 @@ struct pcap_cache;
 
 bool pcap_cache_add_specific(struct pcap_cache **ppcache, const char *name, const char *comment, const char *location);
 void pcap_cache_destroy_specific(struct pcap_cache **ppcache);
-bool pcap_cache_add(const char *name, const char *comment, const char *location);
 bool pcap_cache_loaded(void);
 bool pcap_cache_replace(const struct pcap_cache *cache);
 void pcap_printer_fn_specific(const struct pcap_cache *, void (*fn)(const char *, const char *, const char *, void *), void *);
-- 
2.1.0


From 10582491e417d5ab5c77afe2337793dbacd98fa8 Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss@samba.org>
Date: Wed, 23 Jul 2014 12:12:34 +0200
Subject: [PATCH 5/7] PATCHSET18: printing: return last change time with
 pcap_cache_loaded()

Bug: https://bugzilla.samba.org/show_bug.cgi?id=10652

Signed-off-by: David Disseldorp <ddiss@samba.org>
---
 source3/printing/load.c |  2 +-
 source3/printing/pcap.c | 10 ++++++++--
 source3/printing/pcap.h |  2 +-
 source3/web/swat.c      |  4 ++--
 4 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/source3/printing/load.c b/source3/printing/load.c
index 0a3de73..83f1095 100644
--- a/source3/printing/load.c
+++ b/source3/printing/load.c
@@ -64,7 +64,7 @@ load automatic printer services from pre-populated pcap cache
 void load_printers(struct tevent_context *ev,
 		   struct messaging_context *msg_ctx)
 {
-	SMB_ASSERT(pcap_cache_loaded());
+	SMB_ASSERT(pcap_cache_loaded(NULL));
 
 	add_auto_printers();
 
diff --git a/source3/printing/pcap.c b/source3/printing/pcap.c
index 5059f20..027c1b2 100644
--- a/source3/printing/pcap.c
+++ b/source3/printing/pcap.c
@@ -83,13 +83,19 @@ void pcap_cache_destroy_specific(struct pcap_cache **pp_cache)
 	*pp_cache = NULL;
 }
 
-bool pcap_cache_loaded(void)
+bool pcap_cache_loaded(time_t *_last_change)
 {
 	NTSTATUS status;
 	time_t last;
 
 	status = printer_list_get_last_refresh(&last);
-	return NT_STATUS_IS_OK(status);
+	if (!NT_STATUS_IS_OK(status)) {
+		return false;
+	}
+	if (_last_change != NULL) {
+		*_last_change = last;
+	}
+	return true;
 }
 
 bool pcap_cache_replace(const struct pcap_cache *pcache)
diff --git a/source3/printing/pcap.h b/source3/printing/pcap.h
index 7dccf84..8fc9e9d 100644
--- a/source3/printing/pcap.h
+++ b/source3/printing/pcap.h
@@ -35,7 +35,7 @@ struct pcap_cache;
 
 bool pcap_cache_add_specific(struct pcap_cache **ppcache, const char *name, const char *comment, const char *location);
 void pcap_cache_destroy_specific(struct pcap_cache **ppcache);
-bool pcap_cache_loaded(void);
+bool pcap_cache_loaded(time_t *_last_change);
 bool pcap_cache_replace(const struct pcap_cache *cache);
 void pcap_printer_fn_specific(const struct pcap_cache *, void (*fn)(const char *, const char *, const char *, void *), void *);
 void pcap_printer_read_fn(void (*fn)(const char *, const char *, const char *, void *), void *);
diff --git a/source3/web/swat.c b/source3/web/swat.c
index f8933d2..a1a035c 100644
--- a/source3/web/swat.c
+++ b/source3/web/swat.c
@@ -586,7 +586,7 @@ static int save_reload(int snum)
                 return 0;
         }
 	iNumNonAutoPrintServices = lp_numservices();
-	if (pcap_cache_loaded()) {
+	if (pcap_cache_loaded(NULL)) {
 		load_printers(server_event_context(),
 			      server_messaging_context());
 	}
@@ -1572,7 +1572,7 @@ const char *lang_msg_rotate(TALLOC_CTX *ctx, const char *msgid)
 	reopen_logs();
 	load_interfaces();
 	iNumNonAutoPrintServices = lp_numservices();
-	if (pcap_cache_loaded()) {
+	if (pcap_cache_loaded(NULL)) {
 		load_printers(server_event_context(),
 			      server_messaging_context());
 	}
-- 
2.1.0


From 484667ff73b54b275f8629264aef27ec9628c7fd Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss@samba.org>
Date: Wed, 23 Jul 2014 14:42:00 +0200
Subject: [PATCH 6/7] PATCHSET18: smbd: only reprocess printer_list.tdb if it
 changed

The per-client smbd printer share inventory is currently updated from
printer_list.tdb when a client enumerates printers, via EnumPrinters or
NetShareEnum.
printer_list.tdb is populated by the background print process, based on
the latest printcap values retrieved from the printing backend (e.g.
CUPS) at regular intervals.
This change ensures that per-client smbd processes don't reparse
printer_list.tdb if it hasn't been updated since the last enumeration.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=10652

Suggested-by: Volker Lendecke <vl@samba.org>
Signed-off-by: David Disseldorp <ddiss@samba.org>
---
 source3/smbd/server_reload.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/source3/smbd/server_reload.c b/source3/smbd/server_reload.c
index c4c5a8d..57f7972 100644
--- a/source3/smbd/server_reload.c
+++ b/source3/smbd/server_reload.c
@@ -30,6 +30,13 @@
 #include "auth.h"
 #include "messages.h"
 
+/*
+ * The persistent pcap cache is populated by the background print process. Per
+ * client smbds should only reload their printer share inventories if this
+ * information has changed. Use last_reload_time to detect this.
+ */
+static time_t reload_last_pcap_time = 0;
+
 /****************************************************************************
  purge stale printers and reload from pre-populated pcap cache
 **************************************************************************/
@@ -40,6 +47,20 @@ void reload_printers(struct tevent_context *ev,
 	int pnum;
 	int snum;
 	const char *pname;
+	bool ok;
+	time_t pcap_last_update;
+
+	ok = pcap_cache_loaded(&pcap_last_update);
+	if (!ok) {
+		DEBUG(1, ("pcap cache not loaded\n"));
+		return;
+	}
+
+	if (reload_last_pcap_time == pcap_last_update) {
+		DEBUG(5, ("skipping printer reload, already up to date.\n"));
+		return;
+	}
+	reload_last_pcap_time = pcap_last_update;
 
 	n_services = lp_numservices();
 	pnum = lp_servicenumber(PRINTERS_NAME);
-- 
2.1.0


From 08848f939b735b5a68066ebcc995247d77f5fa2d Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss@samba.org>
Date: Wed, 6 Aug 2014 14:33:02 +0200
Subject: [PATCH 7/7] PATCHSET18: printing: reload printer shares on
 OpenPrinter

The printer share inventory should be reloaded on open _and_
enumeration, as there are some clients, such as cupsaddsmb, that do not
perform an enumeration prior to access.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=10652

Signed-off-by: David Disseldorp <ddiss@samba.org>
---
 source3/rpc_server/spoolss/srv_spoolss_nt.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/source3/rpc_server/spoolss/srv_spoolss_nt.c b/source3/rpc_server/spoolss/srv_spoolss_nt.c
index db48574..fb8f61f 100644
--- a/source3/rpc_server/spoolss/srv_spoolss_nt.c
+++ b/source3/rpc_server/spoolss/srv_spoolss_nt.c
@@ -1737,6 +1737,16 @@ WERROR _spoolss_OpenPrinterEx(struct pipes_struct *p,
 		return WERR_INVALID_PARAM;
 	}
 
+	/*
+	 * The printcap printer share inventory is updated on client
+	 * enumeration. For clients that do not perform enumeration prior to
+	 * access, such as cupssmbadd, we reinitialise the printer share
+	 * inventory on open as well.
+	 */
+	become_root();
+	reload_printers(messaging_event_context(p->msg_ctx), p->msg_ctx);
+	unbecome_root();
+
 	/* some sanity check because you can open a printer or a print server */
 	/* aka: \\server\printer or \\server */
 
@@ -4323,7 +4333,7 @@ static WERROR enum_all_printers_info_level(TALLOC_CTX *mem_ctx,
 	struct dcerpc_binding_handle *b = NULL;
 
 	/*
-	 * printer shares are only updated on client enumeration. The background
+	 * printer shares are updated on client enumeration. The background
 	 * printer process updates printer_list.tdb at regular intervals.
 	 */
 	become_root();
-- 
2.1.0

