mirror of
https://github.com/samba-team/samba.git
synced 2024-12-24 21:34:56 +03:00
a6ff0d29d6
Jeremy.
(This used to be commit a33c395c98
)
1124 lines
30 KiB
C
1124 lines
30 KiB
C
#define OLD_NTDOMAIN 1
|
|
/*
|
|
Unix SMB/Netbios implementation.
|
|
Version 3.0
|
|
printing backend routines
|
|
Copyright (C) Andrew Tridgell 1992-2000
|
|
|
|
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"
|
|
extern int DEBUGLEVEL;
|
|
|
|
/*
|
|
the printing backend revolves around a tdb database that stores the
|
|
SMB view of the print queue
|
|
|
|
The key for this database is a jobid - a internally generated number that
|
|
uniquely identifies a print job
|
|
|
|
reading the print queue involves two steps:
|
|
- possibly running lpq and updating the internal database from that
|
|
- reading entries from the database
|
|
|
|
jobids are assigned when a job starts spooling.
|
|
*/
|
|
|
|
struct printjob {
|
|
pid_t pid; /* which process launched the job */
|
|
int sysjob; /* the system (lp) job number */
|
|
int fd; /* file descriptor of open file if open */
|
|
time_t starttime; /* when the job started spooling */
|
|
int status; /* the status of this job */
|
|
size_t size; /* the size of the job so far */
|
|
BOOL spooled; /* has it been sent to the spooler yet? */
|
|
BOOL smbjob; /* set if the job is a SMB job */
|
|
fstring filename; /* the filename used to spool the file */
|
|
fstring jobname; /* the job name given to us by the client */
|
|
fstring user; /* the user who started the job */
|
|
fstring qname; /* name of the print queue the job was sent to */
|
|
};
|
|
|
|
/* the open printing.tdb database */
|
|
static TDB_CONTEXT *tdb;
|
|
static pid_t local_pid;
|
|
|
|
#define PRINT_MAX_JOBID 10000
|
|
#define UNIX_JOB_START PRINT_MAX_JOBID
|
|
|
|
#define PRINT_SPOOL_PREFIX "smbprn."
|
|
#define PRINT_DATABASE_VERSION 2
|
|
|
|
/****************************************************************************
|
|
initialise the printing backend. Called once at startup.
|
|
Does not survive a fork
|
|
****************************************************************************/
|
|
BOOL print_backend_init(void)
|
|
{
|
|
char *sversion = "INFO/version";
|
|
|
|
if (tdb && local_pid == sys_getpid()) return True;
|
|
tdb = tdb_open(lock_path("printing.tdb"), 0, 0, O_RDWR|O_CREAT, 0600);
|
|
if (!tdb) {
|
|
DEBUG(0,("Failed to open printing backend database\n"));
|
|
}
|
|
local_pid = sys_getpid();
|
|
|
|
/* handle a Samba upgrade */
|
|
tdb_lock_bystring(tdb, sversion);
|
|
if (tdb_fetch_int(tdb, sversion) != PRINT_DATABASE_VERSION) {
|
|
tdb_traverse(tdb, (tdb_traverse_func)tdb_delete, NULL);
|
|
tdb_store_int(tdb, sversion, PRINT_DATABASE_VERSION);
|
|
}
|
|
tdb_unlock_bystring(tdb, sversion);
|
|
|
|
return nt_printing_init();
|
|
}
|
|
|
|
/****************************************************************************
|
|
useful function to generate a tdb key
|
|
****************************************************************************/
|
|
static TDB_DATA print_key(int jobid)
|
|
{
|
|
static int j;
|
|
TDB_DATA ret;
|
|
|
|
j = jobid;
|
|
ret.dptr = (void *)&j;
|
|
ret.dsize = sizeof(j);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
useful function to find a print job in the database
|
|
****************************************************************************/
|
|
static struct printjob *print_job_find(int jobid)
|
|
{
|
|
static struct printjob pjob;
|
|
TDB_DATA ret;
|
|
|
|
ret = tdb_fetch(tdb, print_key(jobid));
|
|
if (!ret.dptr || ret.dsize != sizeof(pjob)) return NULL;
|
|
|
|
memcpy(&pjob, ret.dptr, sizeof(pjob));
|
|
free(ret.dptr);
|
|
return &pjob;
|
|
}
|
|
|
|
/****************************************************************************
|
|
store a job structure back to the database
|
|
****************************************************************************/
|
|
static BOOL print_job_store(int jobid, struct printjob *pjob)
|
|
{
|
|
TDB_DATA d;
|
|
d.dptr = (void *)pjob;
|
|
d.dsize = sizeof(*pjob);
|
|
|
|
return (0 == tdb_store(tdb, print_key(jobid), d, TDB_REPLACE));
|
|
}
|
|
|
|
/****************************************************************************
|
|
run a given print command
|
|
a null terminated list of value/substitute pairs is provided
|
|
for local substitution strings
|
|
****************************************************************************/
|
|
static int print_run_command(int snum,char *command,
|
|
char *outfile,
|
|
...)
|
|
{
|
|
pstring syscmd;
|
|
char *p, *arg;
|
|
int ret;
|
|
va_list ap;
|
|
|
|
if (!command || !*command) return -1;
|
|
|
|
if (!VALID_SNUM(snum)) {
|
|
DEBUG(0,("Invalid snum %d for command %s\n", snum, command));
|
|
return -1;
|
|
}
|
|
|
|
pstrcpy(syscmd, command);
|
|
|
|
va_start(ap, outfile);
|
|
while ((arg = va_arg(ap, char *))) {
|
|
char *value = va_arg(ap,char *);
|
|
pstring_sub(syscmd, arg, value);
|
|
}
|
|
va_end(ap);
|
|
|
|
p = PRINTERNAME(snum);
|
|
if (!p || !*p) p = SERVICE(snum);
|
|
|
|
pstring_sub(syscmd, "%p", p);
|
|
standard_sub_snum(snum,syscmd);
|
|
|
|
ret = smbrun(syscmd,outfile,False);
|
|
|
|
DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
|
|
return ret;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
parse a file name from the system spooler to generate a jobid
|
|
****************************************************************************/
|
|
static int print_parse_jobid(char *fname)
|
|
{
|
|
int jobid;
|
|
|
|
if (strncmp(fname,PRINT_SPOOL_PREFIX,strlen(PRINT_SPOOL_PREFIX)) != 0) return -1;
|
|
fname += strlen(PRINT_SPOOL_PREFIX);
|
|
|
|
jobid = atoi(fname);
|
|
if (jobid <= 0) return -1;
|
|
|
|
return jobid;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
list a unix job in the print database
|
|
****************************************************************************/
|
|
static void print_unix_job(int snum, print_queue_struct *q)
|
|
{
|
|
int jobid = q->job + UNIX_JOB_START;
|
|
struct printjob pj;
|
|
|
|
/* Don't re-insert a unix job if it already exists as it mucks
|
|
up the timestamp. */
|
|
|
|
if (tdb_exists(tdb, print_key(jobid))) {
|
|
return;
|
|
}
|
|
|
|
ZERO_STRUCT(pj);
|
|
|
|
pj.pid = (pid_t)-1;
|
|
pj.sysjob = q->job;
|
|
pj.fd = -1;
|
|
pj.starttime = q->time;
|
|
pj.status = q->status;
|
|
pj.size = q->size;
|
|
pj.spooled = True;
|
|
pj.smbjob = False;
|
|
fstrcpy(pj.filename, "");
|
|
fstrcpy(pj.jobname, q->file);
|
|
fstrcpy(pj.user, q->user);
|
|
fstrcpy(pj.qname, lp_servicename(snum));
|
|
|
|
print_job_store(jobid, &pj);
|
|
}
|
|
|
|
|
|
struct traverse_struct {
|
|
print_queue_struct *queue;
|
|
int qcount, snum, maxcount;
|
|
};
|
|
|
|
/* utility fn to delete any jobs that are no longer active */
|
|
static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
|
|
{
|
|
struct traverse_struct *ts = (struct traverse_struct *)state;
|
|
struct printjob pjob;
|
|
int i, jobid;
|
|
|
|
if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) return 0;
|
|
memcpy(&jobid, key.dptr, sizeof(jobid));
|
|
memcpy(&pjob, data.dptr, sizeof(pjob));
|
|
|
|
if (strcmp(lp_servicename(ts->snum), pjob.qname)) {
|
|
/* this isn't for the queue we are looking at */
|
|
return 0;
|
|
}
|
|
|
|
if (!pjob.smbjob) {
|
|
/* remove a unix job if it isn't in the system queue
|
|
any more */
|
|
for (i=0;i<ts->qcount;i++) {
|
|
if (jobid == ts->queue[i].job + UNIX_JOB_START) break;
|
|
}
|
|
if (i == ts->qcount) tdb_delete(tdb, key);
|
|
return 0;
|
|
}
|
|
|
|
/* maybe it hasn't been spooled yet */
|
|
if (!pjob.spooled) {
|
|
/* if a job is not spooled and the process doesn't
|
|
exist then kill it. This cleans up after smbd
|
|
deaths */
|
|
if (!process_exists(pjob.pid)) {
|
|
tdb_delete(tdb, key);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
for (i=0;i<ts->qcount;i++) {
|
|
int qid = print_parse_jobid(ts->queue[i].file);
|
|
if (jobid == qid) break;
|
|
}
|
|
|
|
if (i == ts->qcount) {
|
|
/* the job isn't in the system queue - we have to
|
|
assume it has completed, so delete the database
|
|
entry */
|
|
tdb_delete(t, key);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
check if the print queue has been updated recently enough
|
|
****************************************************************************/
|
|
static void print_cache_flush(int snum)
|
|
{
|
|
fstring key;
|
|
slprintf(key, sizeof(key), "CACHE/%s", lp_servicename(snum));
|
|
tdb_store_int(tdb, key, -1);
|
|
}
|
|
|
|
/****************************************************************************
|
|
update the internal database from the system print queue for a queue
|
|
****************************************************************************/
|
|
static void print_queue_update(int snum)
|
|
{
|
|
char *path = lp_pathname(snum);
|
|
char *cmd = lp_lpqcommand(snum);
|
|
char **qlines;
|
|
pstring tmp_file;
|
|
int numlines, i, qcount;
|
|
print_queue_struct *queue = NULL;
|
|
print_status_struct status;
|
|
struct printjob *pjob;
|
|
struct traverse_struct tstruct;
|
|
fstring keystr;
|
|
TDB_DATA data, key;
|
|
|
|
/*
|
|
* Update the cache time FIRST ! Stops others doing this
|
|
* if the lpq takes a long time.
|
|
*/
|
|
|
|
slprintf(keystr, sizeof(keystr), "CACHE/%s", lp_servicename(snum));
|
|
tdb_store_int(tdb, keystr, (int)time(NULL));
|
|
|
|
slprintf(tmp_file, sizeof(tmp_file), "%s/smblpq.%d", path, local_pid);
|
|
|
|
unlink(tmp_file);
|
|
print_run_command(snum, cmd, tmp_file,
|
|
NULL);
|
|
|
|
numlines = 0;
|
|
qlines = file_lines_load(tmp_file, &numlines);
|
|
unlink(tmp_file);
|
|
|
|
/* turn the lpq output into a series of job structures */
|
|
qcount = 0;
|
|
ZERO_STRUCT(status);
|
|
if (numlines)
|
|
queue = (print_queue_struct *)malloc(sizeof(print_queue_struct)*(numlines+1));
|
|
|
|
if (queue) {
|
|
for (i=0; i<numlines; i++) {
|
|
/* parse the line */
|
|
if (parse_lpq_entry(snum,qlines[i],
|
|
&queue[qcount],&status,qcount==0)) {
|
|
qcount++;
|
|
}
|
|
}
|
|
}
|
|
file_lines_free(qlines);
|
|
|
|
/*
|
|
any job in the internal database that is marked as spooled
|
|
and doesn't exist in the system queue is considered finished
|
|
and removed from the database
|
|
|
|
any job in the system database but not in the internal database
|
|
is added as a unix job
|
|
|
|
fill in any system job numbers as we go
|
|
*/
|
|
for (i=0; i<qcount; i++) {
|
|
int jobid = print_parse_jobid(queue[i].file);
|
|
|
|
if (jobid == -1) {
|
|
/* assume its a unix print job */
|
|
print_unix_job(snum, &queue[i]);
|
|
continue;
|
|
}
|
|
|
|
/* we have an active SMB print job - update its status */
|
|
pjob = print_job_find(jobid);
|
|
if (!pjob) {
|
|
/* err, somethings wrong. Probably smbd was restarted
|
|
with jobs in the queue. All we can do is treat them
|
|
like unix jobs. Pity. */
|
|
print_unix_job(snum, &queue[i]);
|
|
continue;
|
|
}
|
|
|
|
pjob->sysjob = queue[i].job;
|
|
pjob->status = queue[i].status;
|
|
|
|
print_job_store(jobid, pjob);
|
|
}
|
|
|
|
/* now delete any queued entries that don't appear in the
|
|
system queue */
|
|
tstruct.queue = queue;
|
|
tstruct.qcount = qcount;
|
|
tstruct.snum = snum;
|
|
|
|
tdb_traverse(tdb, traverse_fn_delete, (void *)&tstruct);
|
|
|
|
safe_free(tstruct.queue);
|
|
|
|
/* store the queue status structure */
|
|
status.qcount = qcount;
|
|
slprintf(keystr, sizeof(keystr), "STATUS/%s", lp_servicename(snum));
|
|
data.dptr = (void *)&status;
|
|
data.dsize = sizeof(status);
|
|
key.dptr = keystr;
|
|
key.dsize = strlen(keystr);
|
|
tdb_store(tdb, key, data, TDB_REPLACE);
|
|
|
|
/*
|
|
* Update the cache time again. We want to do this call
|
|
* as little as possible...
|
|
*/
|
|
|
|
slprintf(keystr, sizeof(keystr), "CACHE/%s", lp_servicename(snum));
|
|
tdb_store_int(tdb, keystr, (int)time(NULL));
|
|
}
|
|
|
|
/****************************************************************************
|
|
check if a jobid is valid. It is valid if it exists in the database
|
|
****************************************************************************/
|
|
BOOL print_job_exists(int jobid)
|
|
{
|
|
return tdb_exists(tdb, print_key(jobid));
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
work out which service a jobid is for
|
|
note that we have to look up by queue name to ensure that it works for
|
|
other than the process that started the job
|
|
****************************************************************************/
|
|
int print_job_snum(int jobid)
|
|
{
|
|
struct printjob *pjob = print_job_find(jobid);
|
|
if (!pjob) return -1;
|
|
|
|
return lp_servicenumber(pjob->qname);
|
|
}
|
|
|
|
/****************************************************************************
|
|
give the fd used for a jobid
|
|
****************************************************************************/
|
|
int print_job_fd(int jobid)
|
|
{
|
|
struct printjob *pjob = print_job_find(jobid);
|
|
if (!pjob) return -1;
|
|
/* don't allow another process to get this info - it is meaningless */
|
|
if (pjob->pid != local_pid) return -1;
|
|
return pjob->fd;
|
|
}
|
|
|
|
/****************************************************************************
|
|
give the filename used for a jobid
|
|
only valid for the process doing the spooling and when the job
|
|
has not been spooled
|
|
****************************************************************************/
|
|
char *print_job_fname(int jobid)
|
|
{
|
|
struct printjob *pjob = print_job_find(jobid);
|
|
if (!pjob || pjob->spooled || pjob->pid != local_pid) return NULL;
|
|
return pjob->filename;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
set the place in the queue for a job
|
|
****************************************************************************/
|
|
BOOL print_job_set_place(int jobid, int place)
|
|
{
|
|
DEBUG(2,("print_job_set_place not implemented yet\n"));
|
|
return False;
|
|
}
|
|
|
|
/****************************************************************************
|
|
set the name of a job. Only possible for owner
|
|
****************************************************************************/
|
|
BOOL print_job_set_name(int jobid, char *name)
|
|
{
|
|
struct printjob *pjob = print_job_find(jobid);
|
|
if (!pjob || pjob->pid != local_pid) return False;
|
|
|
|
fstrcpy(pjob->jobname, name);
|
|
return print_job_store(jobid, pjob);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
delete a print job - don't update queue
|
|
****************************************************************************/
|
|
static BOOL print_job_delete1(int jobid)
|
|
{
|
|
struct printjob *pjob = print_job_find(jobid);
|
|
int snum;
|
|
|
|
if (!pjob) return False;
|
|
|
|
snum = print_job_snum(jobid);
|
|
|
|
if (pjob->spooled && pjob->sysjob != -1) {
|
|
/* need to delete the spooled entry */
|
|
fstring jobstr;
|
|
slprintf(jobstr, sizeof(jobstr), "%d", pjob->sysjob);
|
|
print_run_command(snum,
|
|
lp_lprmcommand(snum), NULL,
|
|
"%j", jobstr,
|
|
"%T", http_timestring(pjob->starttime),
|
|
NULL);
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
/****************************************************************************
|
|
return true if the current user owns the print job
|
|
****************************************************************************/
|
|
static BOOL is_owner(struct current_user *user, int jobid)
|
|
{
|
|
struct printjob *pjob = print_job_find(jobid);
|
|
user_struct *vuser;
|
|
|
|
if (!pjob || !user) return False;
|
|
|
|
if ((vuser = get_valid_user_struct(user->vuid)) != NULL) {
|
|
return strequal(pjob->user, vuser->user.smb_name);
|
|
} else {
|
|
return strequal(pjob->user, uidtoname(user->uid));
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
delete a print job
|
|
****************************************************************************/
|
|
BOOL print_job_delete(struct current_user *user, int jobid)
|
|
{
|
|
int snum = print_job_snum(jobid);
|
|
BOOL owner;
|
|
|
|
owner = is_owner(user, jobid);
|
|
|
|
/* Check access against security descriptor or whether the user
|
|
owns their job. */
|
|
|
|
if (!owner &&
|
|
!print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
|
|
DEBUG(3, ("delete denied by security descriptor\n"));
|
|
return False;
|
|
}
|
|
|
|
if (!print_job_delete1(jobid)) return False;
|
|
|
|
/* force update the database and say the delete failed if the
|
|
job still exists */
|
|
|
|
print_queue_update(snum);
|
|
|
|
return !print_job_exists(jobid);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
pause a job
|
|
****************************************************************************/
|
|
BOOL print_job_pause(struct current_user *user, int jobid)
|
|
{
|
|
struct printjob *pjob = print_job_find(jobid);
|
|
int snum, ret = -1;
|
|
fstring jobstr;
|
|
BOOL owner;
|
|
|
|
|
|
if (!pjob || !user) return False;
|
|
|
|
if (!pjob->spooled || pjob->sysjob == -1) return False;
|
|
|
|
snum = print_job_snum(jobid);
|
|
owner = is_owner(user, jobid);
|
|
|
|
if (!owner &&
|
|
!print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
|
|
DEBUG(3, ("pause denied by security descriptor\n"));
|
|
return False;
|
|
}
|
|
|
|
/* need to pause the spooled entry */
|
|
slprintf(jobstr, sizeof(jobstr), "%d", pjob->sysjob);
|
|
ret = print_run_command(snum,
|
|
lp_lppausecommand(snum), NULL,
|
|
"%j", jobstr,
|
|
NULL);
|
|
|
|
/* force update the database */
|
|
print_cache_flush(snum);
|
|
|
|
/* how do we tell if this succeeded? */
|
|
return ret == 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
resume a job
|
|
****************************************************************************/
|
|
BOOL print_job_resume(struct current_user *user, int jobid)
|
|
{
|
|
struct printjob *pjob = print_job_find(jobid);
|
|
int snum, ret;
|
|
fstring jobstr;
|
|
BOOL owner;
|
|
|
|
if (!pjob || !user) return False;
|
|
|
|
if (!pjob->spooled || pjob->sysjob == -1) return False;
|
|
|
|
snum = print_job_snum(jobid);
|
|
owner = is_owner(user, jobid);
|
|
|
|
if (!is_owner(user, jobid) &&
|
|
!print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
|
|
DEBUG(3, ("resume denied by security descriptor\n"));
|
|
return False;
|
|
}
|
|
|
|
slprintf(jobstr, sizeof(jobstr), "%d", pjob->sysjob);
|
|
ret = print_run_command(snum,
|
|
lp_lpresumecommand(snum), NULL,
|
|
"%j", jobstr,
|
|
NULL);
|
|
|
|
/* force update the database */
|
|
print_cache_flush(snum);
|
|
|
|
/* how do we tell if this succeeded? */
|
|
return ret == 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
write to a print file
|
|
****************************************************************************/
|
|
int print_job_write(int jobid, const char *buf, int size)
|
|
{
|
|
int fd;
|
|
|
|
fd = print_job_fd(jobid);
|
|
if (fd == -1) return -1;
|
|
|
|
return write(fd, buf, size);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Check if the print queue has been updated recently enough.
|
|
****************************************************************************/
|
|
|
|
static BOOL print_cache_expired(int snum)
|
|
{
|
|
fstring key;
|
|
time_t t2, t = time(NULL);
|
|
slprintf(key, sizeof(key), "CACHE/%s", lp_servicename(snum));
|
|
t2 = tdb_fetch_int(tdb, key);
|
|
if (t2 == ((time_t)-1) || (t - t2) >= lp_lpqcachetime()) {
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Determine the number of jobs in a queue.
|
|
****************************************************************************/
|
|
|
|
static int print_queue_length(int snum)
|
|
{
|
|
fstring keystr;
|
|
TDB_DATA data, key;
|
|
print_status_struct status;
|
|
|
|
/* make sure the database is up to date */
|
|
if (print_cache_expired(snum)) print_queue_update(snum);
|
|
|
|
/* also fetch the queue status */
|
|
ZERO_STRUCTP(&status);
|
|
slprintf(keystr, sizeof(keystr), "STATUS/%s", lp_servicename(snum));
|
|
key.dptr = keystr;
|
|
key.dsize = strlen(keystr);
|
|
data = tdb_fetch(tdb, key);
|
|
if (data.dptr) {
|
|
if (data.dsize == sizeof(status)) {
|
|
memcpy(&status, data.dptr, sizeof(status));
|
|
}
|
|
free(data.dptr);
|
|
}
|
|
return status.qcount;
|
|
}
|
|
|
|
/***************************************************************************
|
|
start spooling a job - return the jobid
|
|
***************************************************************************/
|
|
int print_job_start(struct current_user *user, int snum, char *jobname)
|
|
{
|
|
int jobid;
|
|
char *path;
|
|
struct printjob pjob;
|
|
int next_jobid;
|
|
user_struct *vuser;
|
|
|
|
errno = 0;
|
|
|
|
if (!print_access_check(user, snum, PRINTER_ACCESS_USE)) {
|
|
DEBUG(3, ("print_job_start: job start denied by security descriptor\n"));
|
|
return -1;
|
|
}
|
|
|
|
if (!print_time_access_check(snum)) {
|
|
DEBUG(3, ("print_job_start: job start denied by time check\n"));
|
|
return -1;
|
|
}
|
|
|
|
path = lp_pathname(snum);
|
|
|
|
/* see if we have sufficient disk space */
|
|
if (lp_minprintspace(snum)) {
|
|
SMB_BIG_UINT dspace, dsize;
|
|
if (sys_fsusage(path, &dspace, &dsize) == 0 &&
|
|
dspace < 2*(SMB_BIG_UINT)lp_minprintspace(snum)) {
|
|
errno = ENOSPC;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* for autoloaded printers, check that the printcap entry still exists */
|
|
if (lp_autoloaded(snum) && !pcap_printername_ok(lp_servicename(snum), NULL)) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
if (print_queue_length(snum) > lp_maxprintjobs(snum)) {
|
|
errno = ENOSPC;
|
|
return -1;
|
|
}
|
|
|
|
/* create the database entry */
|
|
ZERO_STRUCT(pjob);
|
|
pjob.pid = local_pid;
|
|
pjob.sysjob = -1;
|
|
pjob.fd = -1;
|
|
pjob.starttime = time(NULL);
|
|
pjob.status = LPQ_QUEUED;
|
|
pjob.size = 0;
|
|
pjob.spooled = False;
|
|
pjob.smbjob = True;
|
|
|
|
fstrcpy(pjob.jobname, jobname);
|
|
|
|
if ((vuser = get_valid_user_struct(user->vuid)) != NULL) {
|
|
fstrcpy(pjob.user, vuser->user.smb_name);
|
|
} else {
|
|
fstrcpy(pjob.user, uidtoname(user->uid));
|
|
}
|
|
|
|
fstrcpy(pjob.qname, lp_servicename(snum));
|
|
|
|
/* lock the database */
|
|
tdb_lock_bystring(tdb, "INFO/nextjob");
|
|
|
|
next_jobnum:
|
|
next_jobid = tdb_fetch_int(tdb, "INFO/nextjob");
|
|
if (next_jobid == -1) next_jobid = 1;
|
|
|
|
for (jobid = next_jobid+1; jobid != next_jobid; ) {
|
|
if (!print_job_exists(jobid)) break;
|
|
jobid = (jobid + 1) % PRINT_MAX_JOBID;
|
|
if (jobid == 0) jobid = 1;
|
|
}
|
|
if (jobid == next_jobid || !print_job_store(jobid, &pjob)) {
|
|
jobid = -1;
|
|
goto fail;
|
|
}
|
|
|
|
tdb_store_int(tdb, "INFO/nextjob", jobid);
|
|
|
|
/* we have a job entry - now create the spool file
|
|
|
|
we unlink first to cope with old spool files and also to beat
|
|
a symlink security hole - it allows us to use O_EXCL
|
|
There may be old spool files owned by other users lying around.
|
|
*/
|
|
slprintf(pjob.filename, sizeof(pjob.filename), "%s/%s%d",
|
|
path, PRINT_SPOOL_PREFIX, jobid);
|
|
if (unlink(pjob.filename) == -1 && errno != ENOENT) {
|
|
goto next_jobnum;
|
|
}
|
|
pjob.fd = sys_open(pjob.filename,O_WRONLY|O_CREAT|O_EXCL,0600);
|
|
if (pjob.fd == -1) goto fail;
|
|
|
|
print_job_store(jobid, &pjob);
|
|
|
|
tdb_unlock_bystring(tdb, "INFO/nextjob");
|
|
|
|
/*
|
|
* If the printer is marked as postscript output a leading
|
|
* file identifier to ensure the file is treated as a raw
|
|
* postscript file.
|
|
* This has a similar effect as CtrlD=0 in WIN.INI file.
|
|
* tim@fsg.com 09/06/94
|
|
*/
|
|
if (lp_postscript(snum)) {
|
|
print_job_write(jobid, "%!\n",3);
|
|
}
|
|
|
|
return jobid;
|
|
|
|
fail:
|
|
if (jobid != -1) {
|
|
tdb_delete(tdb, print_key(jobid));
|
|
}
|
|
|
|
tdb_unlock_bystring(tdb, "INFO/nextjob");
|
|
return -1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Print a file - called on closing the file. This spools the job.
|
|
****************************************************************************/
|
|
|
|
BOOL print_job_end(int jobid)
|
|
{
|
|
struct printjob *pjob = print_job_find(jobid);
|
|
int snum;
|
|
SMB_STRUCT_STAT sbuf;
|
|
pstring current_directory;
|
|
pstring print_directory;
|
|
char *wd, *p, *printer_name;
|
|
pstring jobname;
|
|
|
|
if (!pjob)
|
|
return False;
|
|
|
|
if (pjob->spooled || pjob->pid != local_pid)
|
|
return False;
|
|
|
|
snum = print_job_snum(jobid);
|
|
|
|
if (sys_fstat(pjob->fd, &sbuf) == 0)
|
|
pjob->size = sbuf.st_size;
|
|
|
|
close(pjob->fd);
|
|
pjob->fd = -1;
|
|
|
|
if (pjob->size == 0) {
|
|
/* don't bother spooling empty files */
|
|
unlink(pjob->filename);
|
|
tdb_delete(tdb, print_key(jobid));
|
|
return True;
|
|
}
|
|
|
|
/* we print from the directory path to give the best chance of
|
|
parsing the lpq output */
|
|
wd = sys_getwd(current_directory);
|
|
if (!wd)
|
|
return False;
|
|
|
|
pstrcpy(print_directory, pjob->filename);
|
|
p = strrchr(print_directory,'/');
|
|
if (!p)
|
|
return False;
|
|
*p++ = 0;
|
|
|
|
if (chdir(print_directory) != 0)
|
|
return False;
|
|
|
|
pstrcpy(jobname, pjob->jobname);
|
|
pstring_sub(jobname, "'", "_");
|
|
|
|
/* send it to the system spooler */
|
|
print_run_command(snum,
|
|
lp_printcommand(snum), NULL,
|
|
"%s", p,
|
|
"%J", jobname,
|
|
"%f", p,
|
|
NULL);
|
|
|
|
chdir(wd);
|
|
|
|
pjob->spooled = True;
|
|
print_job_store(jobid, pjob);
|
|
|
|
/* force update the database */
|
|
print_cache_flush(snum);
|
|
|
|
/* Send a printer notify message */
|
|
|
|
printer_name = PRINTERNAME(snum);
|
|
|
|
message_send_all(MSG_PRINTER_NOTIFY, printer_name,
|
|
strlen(printer_name) + 1);
|
|
|
|
return True;
|
|
}
|
|
|
|
/* utility fn to enumerate the print queue */
|
|
static int traverse_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
|
|
{
|
|
struct traverse_struct *ts = (struct traverse_struct *)state;
|
|
struct printjob pjob;
|
|
int i, jobid;
|
|
|
|
if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) return 0;
|
|
memcpy(&jobid, key.dptr, sizeof(jobid));
|
|
memcpy(&pjob, data.dptr, sizeof(pjob));
|
|
|
|
/* maybe it isn't for this queue */
|
|
if (ts->snum != print_queue_snum(pjob.qname)) return 0;
|
|
|
|
if (ts->qcount >= ts->maxcount) return 0;
|
|
|
|
i = ts->qcount;
|
|
|
|
ts->queue[i].job = jobid;
|
|
ts->queue[i].size = pjob.size;
|
|
ts->queue[i].status = pjob.status;
|
|
ts->queue[i].priority = 1;
|
|
ts->queue[i].time = pjob.starttime;
|
|
fstrcpy(ts->queue[i].user, pjob.user);
|
|
fstrcpy(ts->queue[i].file, pjob.jobname);
|
|
|
|
ts->qcount++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct traverse_count_struct {
|
|
int snum, count;
|
|
};
|
|
|
|
/* utility fn to count the number of entries in the print queue */
|
|
static int traverse_count_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
|
|
{
|
|
struct traverse_count_struct *ts = (struct traverse_count_struct *)state;
|
|
struct printjob pjob;
|
|
int jobid;
|
|
|
|
if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) return 0;
|
|
memcpy(&jobid, key.dptr, sizeof(jobid));
|
|
memcpy(&pjob, data.dptr, sizeof(pjob));
|
|
|
|
/* maybe it isn't for this queue */
|
|
if (ts->snum != print_queue_snum(pjob.qname)) return 0;
|
|
|
|
ts->count++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Sort print jobs by submittal time */
|
|
|
|
static int printjob_comp(print_queue_struct *j1, print_queue_struct *j2)
|
|
{
|
|
/* Silly cases */
|
|
|
|
if (!j1 && !j2) return 0;
|
|
if (!j1) return -1;
|
|
if (!j2) return 1;
|
|
|
|
/* Sort on job start time */
|
|
|
|
if (j1->time == j2->time) return 0;
|
|
return (j1->time > j2->time) ? 1 : -1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
get a printer queue listing
|
|
****************************************************************************/
|
|
int print_queue_status(int snum,
|
|
print_queue_struct **queue,
|
|
print_status_struct *status)
|
|
{
|
|
struct traverse_struct tstruct;
|
|
struct traverse_count_struct tsc;
|
|
fstring keystr;
|
|
TDB_DATA data, key;
|
|
|
|
/* make sure the database is up to date */
|
|
if (print_cache_expired(snum)) print_queue_update(snum);
|
|
|
|
/*
|
|
* Count the number of entries.
|
|
*/
|
|
tsc.count = 0;
|
|
tsc.snum = snum;
|
|
tdb_traverse(tdb, traverse_count_fn_queue, (void *)&tsc);
|
|
|
|
/* Allocate the queue size. */
|
|
if (( tstruct.queue = (print_queue_struct *)malloc(sizeof(print_queue_struct)*tsc.count))
|
|
== NULL)
|
|
return 0;
|
|
|
|
/*
|
|
* Fill in the queue.
|
|
* We need maxcount as the queue size may have changed between
|
|
* the two calls to tdb_traverse.
|
|
*/
|
|
tstruct.qcount = 0;
|
|
tstruct.maxcount = tsc.count;
|
|
tstruct.snum = snum;
|
|
|
|
tdb_traverse(tdb, traverse_fn_queue, (void *)&tstruct);
|
|
|
|
/* also fetch the queue status */
|
|
ZERO_STRUCTP(status);
|
|
slprintf(keystr, sizeof(keystr), "STATUS/%s", lp_servicename(snum));
|
|
key.dptr = keystr;
|
|
key.dsize = strlen(keystr);
|
|
data = tdb_fetch(tdb, key);
|
|
if (data.dptr) {
|
|
if (data.dsize == sizeof(*status)) {
|
|
memcpy(status, data.dptr, sizeof(*status));
|
|
}
|
|
free(data.dptr);
|
|
}
|
|
|
|
/* Sort the queue by submission time otherwise they are displayed
|
|
in hash order. */
|
|
|
|
qsort(tstruct.queue, tstruct.qcount, sizeof(print_queue_struct),
|
|
QSORT_CAST(printjob_comp));
|
|
|
|
*queue = tstruct.queue;
|
|
return tstruct.qcount;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
turn a queue name into a snum
|
|
****************************************************************************/
|
|
int print_queue_snum(char *qname)
|
|
{
|
|
int snum = lp_servicenumber(qname);
|
|
if (snum == -1 || !lp_print_ok(snum)) return -1;
|
|
return snum;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
pause a queue
|
|
****************************************************************************/
|
|
BOOL print_queue_pause(struct current_user *user, int snum, int *errcode)
|
|
{
|
|
int ret;
|
|
|
|
if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
|
|
*errcode = ERROR_ACCESS_DENIED;
|
|
return False;
|
|
}
|
|
|
|
ret = print_run_command(snum, lp_queuepausecommand(snum), NULL,
|
|
NULL);
|
|
|
|
/* force update the database */
|
|
print_cache_flush(snum);
|
|
|
|
/* Send a printer notify message */
|
|
|
|
if (ret == 0) {
|
|
char *printer_name;
|
|
|
|
printer_name = PRINTERNAME(snum);
|
|
|
|
message_send_all(MSG_PRINTER_NOTIFY, printer_name,
|
|
strlen(printer_name) + 1);
|
|
}
|
|
|
|
return ret == 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
resume a queue
|
|
****************************************************************************/
|
|
BOOL print_queue_resume(struct current_user *user, int snum, int *errcode)
|
|
{
|
|
int ret;
|
|
|
|
if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
|
|
*errcode = ERROR_ACCESS_DENIED;
|
|
return False;
|
|
}
|
|
|
|
ret = print_run_command(snum, lp_queueresumecommand(snum), NULL,
|
|
NULL);
|
|
|
|
/* force update the database */
|
|
print_cache_flush(snum);
|
|
|
|
/* Send a printer notify message */
|
|
|
|
if (ret == 0) {
|
|
char *printer_name;
|
|
|
|
printer_name = PRINTERNAME(snum);
|
|
|
|
message_send_all(MSG_PRINTER_NOTIFY, printer_name,
|
|
strlen(printer_name) + 1);
|
|
}
|
|
|
|
return ret == 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
purge a queue - implemented by deleting all jobs that we can delete
|
|
****************************************************************************/
|
|
BOOL print_queue_purge(struct current_user *user, int snum, int *errcode)
|
|
{
|
|
print_queue_struct *queue;
|
|
print_status_struct status;
|
|
char *printer_name;
|
|
int njobs, i;
|
|
|
|
njobs = print_queue_status(snum, &queue, &status);
|
|
for (i=0;i<njobs;i++) {
|
|
if (print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
|
|
print_job_delete1(queue[i].job);
|
|
}
|
|
}
|
|
|
|
print_cache_flush(snum);
|
|
safe_free(queue);
|
|
|
|
/* Send a printer notify message */
|
|
|
|
printer_name = PRINTERNAME(snum);
|
|
|
|
message_send_all(MSG_PRINTER_NOTIFY, printer_name,
|
|
strlen(printer_name) + 1);
|
|
|
|
return True;
|
|
}
|
|
#undef OLD_NTDOMAIN
|