1 |
teddy |
1.1 |
#!/usr/bin/python |
2 |
|
|
# |
3 |
|
|
# lpr-agent - keep a password in memory and use it to print stuff |
4 |
|
|
# Copyright (C) 2000 Teddy Hogeborn |
5 |
|
|
# |
6 |
|
|
# This program is free software; you can redistribute it and/or modify |
7 |
|
|
# it under the terms of the GNU General Public License as published by |
8 |
|
|
# the Free Software Foundation; either version 2 of the License, or |
9 |
|
|
# (at your option) any later version. |
10 |
|
|
# |
11 |
|
|
# This program is distributed in the hope that it will be useful, |
12 |
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
|
|
# GNU General Public License for more details. |
15 |
|
|
# |
16 |
|
|
# You should have received a copy of the GNU General Public License |
17 |
|
|
# along with this program; if not, write to the Free Software |
18 |
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 |
|
|
# |
20 |
|
|
# Author: Teddy Hogeborn <teddy@fukt.hk-r.se> |
21 |
|
|
# Folkparksv. 22 |
22 |
|
|
# 372 38 Ronneby |
23 |
|
|
# SWEDEN |
24 |
|
|
|
25 |
|
|
import os, errno, tempfile, sys, getopt, SocketServer, rfc822, string |
26 |
|
|
from socket import * |
27 |
|
|
from signal import * |
28 |
|
|
|
29 |
|
|
# Parse the command line options and set the "c_flag" accordingly |
30 |
|
|
optlist, args = getopt.getopt(sys.argv[1:], 'bck', ['sh', 'bourne-shell',\ |
31 |
|
|
'csh', 'c-shell',\ |
32 |
|
|
'debug', 'kill', \ |
33 |
|
|
'version', 'help']) |
34 |
|
|
|
35 |
|
|
if ('--version', '') in optlist: |
36 |
|
|
print """lpr-agent (lpr-agent) 1.0 |
37 |
|
|
Copyright (C) 2000 Teddy Hogeborn |
38 |
|
|
lpr-agent comes with ABSOLUTELY NO WARRANTY. |
39 |
|
|
You may redistribute copies of lpr-agent under the terms of the GNU |
40 |
|
|
General Public License. For more information about these matters, see |
41 |
|
|
the file named COPYING.""" |
42 |
|
|
sys.exit(0) |
43 |
|
|
|
44 |
|
|
if ('--help', '') in optlist: |
45 |
|
|
print """lpr-agent prints some shell commands to be evaluated and also forks |
46 |
|
|
off and stays in memory, receiving commands and data on a Unix file |
47 |
|
|
system socket. It can then be accessed with the program "lpr-client". |
48 |
|
|
|
49 |
|
|
Usage: lpr-agent [options] |
50 |
|
|
|
51 |
|
|
Examples of common usage: |
52 |
|
|
Start a server and evaluate commands |
53 |
|
|
eval `lpr-agent` |
54 |
|
|
Start a server and evaluate commands suitable for C-shells |
55 |
|
|
eval `lpr-agent --c-shell` |
56 |
|
|
Kill the agent (when logging out or when not printing anymore): |
57 |
|
|
lpr-agent --kill |
58 |
|
|
|
59 |
|
|
Normal options: |
60 |
|
|
-k, --kill kill the agent |
61 |
|
|
-b, --sh, --bourne-shell print commands suitable for |
62 |
|
|
bourne shells |
63 |
|
|
-c, --csh, --c-shell print commands suitable for |
64 |
|
|
c-shells |
65 |
|
|
--version show version number |
66 |
|
|
--help show this help |
67 |
|
|
|
68 |
|
|
Obscure or debugging options: |
69 |
|
|
--debug do not close stdout and stderr, |
70 |
|
|
show debugging information on |
71 |
|
|
stdout and lets any Python |
72 |
|
|
errors show up on stderr |
73 |
|
|
|
74 |
|
|
Report bugs to <teddy@fukt.hk-r.se>. |
75 |
|
|
""" |
76 |
|
|
sys.exit(0) |
77 |
|
|
|
78 |
|
|
debug_flag = ('--debug', '') in optlist |
79 |
|
|
|
80 |
|
|
c_flag = (('-c', '') in optlist) or (('--csh', '') in optlist)\ |
81 |
|
|
or (('--c-shell', '') in optlist) |
82 |
|
|
|
83 |
|
|
if (('-k', '') in optlist or ('--kill', '') in optlist): |
84 |
|
|
if os.environ.has_key('LPR_AGENT_PID'): |
85 |
|
|
try: |
86 |
|
|
os.kill(string.atoi(os.environ['LPR_AGENT_PID']), SIGTERM) |
87 |
|
|
except os.error, the_error: |
88 |
|
|
if the_error[0] != errno.ESRCH: |
89 |
|
|
raise os.error, the_error |
90 |
|
|
else: |
91 |
|
|
print "The agent is not running." |
92 |
|
|
sys.exit(1) |
93 |
|
|
else: |
94 |
|
|
print "No agent is running." |
95 |
|
|
sys.exit(1) |
96 |
|
|
sys.exit(0) |
97 |
|
|
|
98 |
|
|
# Make up a name for the directory. |
99 |
|
|
if os.environ.has_key('TMPDIR'): |
100 |
|
|
tempfile.template=os.environ['TMPDIR'] |
101 |
|
|
else: |
102 |
|
|
tempfile.tempdir="/tmp" |
103 |
|
|
tempfile.template="lpr-" + `os.getpid()` + "-" |
104 |
|
|
socket_dir=tempfile.mktemp() |
105 |
|
|
|
106 |
|
|
# Try and create the directory, but don't fail if it already exists. |
107 |
|
|
try: |
108 |
|
|
os.mkdir(socket_dir, 0700) |
109 |
|
|
except os.error, the_error: |
110 |
|
|
if the_error[0] != errno.EEXIST: |
111 |
|
|
raise os.error, the_error |
112 |
|
|
else: |
113 |
|
|
# If the directory alread exists we should be able to change |
114 |
|
|
# the permissions on it. If we can't, the directory is owned |
115 |
|
|
# by someone else who is trying to fool us into using it. |
116 |
|
|
os.chmod(socket_dir, 0700) |
117 |
|
|
|
118 |
|
|
# Make up a name for the file/socket. |
119 |
|
|
tempfile.tempdir=socket_dir |
120 |
|
|
tempfile.template="agent." + `os.getpid()` + "-" |
121 |
|
|
socket_name=tempfile.mktemp() |
122 |
|
|
|
123 |
|
|
def cleanup_socket(): |
124 |
|
|
"Close socket, remove file and directory; called on exit." |
125 |
|
|
try: |
126 |
|
|
sock.shutdown(2) |
127 |
|
|
except os.error, the_error: |
128 |
|
|
pass |
129 |
|
|
try: |
130 |
|
|
os.remove(socket_name) |
131 |
|
|
except os.error, the_error: |
132 |
|
|
pass |
133 |
|
|
try: |
134 |
|
|
os.rmdir(socket_dir) |
135 |
|
|
except os.error, the_error: |
136 |
|
|
pass |
137 |
|
|
|
138 |
|
|
# Try and create an unnamed socket. If we fail we call the cleanup |
139 |
|
|
# function to remove the directory and then call the default error |
140 |
|
|
# handler, which should abort. (The file/socket hasn't been created |
141 |
|
|
# yet.) |
142 |
|
|
try: |
143 |
|
|
sock = socket(AF_UNIX, SOCK_STREAM) |
144 |
|
|
except os.error, the_error: |
145 |
|
|
cleanup_socket() |
146 |
|
|
raise os.error, the_error |
147 |
|
|
|
148 |
|
|
# Fork off a child daemon process; In the parent, just print some |
149 |
|
|
# commands to be eval'ed by a shell and then exit. |
150 |
|
|
pid=os.fork() |
151 |
|
|
if pid != 0: |
152 |
|
|
if c_flag: |
153 |
|
|
print "setenv LPR_AUTH_SOCK " + socket_name + ";" |
154 |
|
|
print "setenv LPR_AGENT_PID " + `pid` + ";" |
155 |
|
|
print "echo Agent pid " + `pid` + ";" |
156 |
|
|
else: |
157 |
|
|
print "LPR_AUTH_SOCK=" + socket_name + "; export LPR_AUTH_SOCK;" |
158 |
|
|
print "LPR_AGENT_PID=" + `pid` + "; export LPR_AGENT_PID;" |
159 |
|
|
print "echo Agent pid " + `pid` + ";" |
160 |
|
|
sys.exit(0) |
161 |
|
|
|
162 |
|
|
# We want to become a daemon now. |
163 |
|
|
|
164 |
|
|
# Close all standard open file descriptors |
165 |
|
|
#sys.stdin.close() |
166 |
|
|
null=os.open("/dev/null", os.O_NOCTTY | os.O_RDONLY) |
167 |
|
|
os.dup2(null, sys.stdin.fileno()) |
168 |
|
|
os.close(null) |
169 |
|
|
if not debug_flag: |
170 |
|
|
# sys.stdout.close() |
171 |
|
|
# sys.stderr.close() |
172 |
|
|
null=os.open("/dev/null", os.O_NOCTTY | os.O_WRONLY) |
173 |
|
|
os.dup2(null, sys.stdout.fileno()) |
174 |
|
|
os.dup2(null, sys.stderr.fileno()) |
175 |
|
|
os.close(null) |
176 |
|
|
|
177 |
|
|
# Create a new process group for this process |
178 |
|
|
os.setsid() |
179 |
|
|
|
180 |
|
|
# On exit remove the socket and the directory |
181 |
|
|
sys.exitfunc=cleanup_socket |
182 |
|
|
|
183 |
|
|
signal(SIGINT, SIG_IGN) |
184 |
|
|
|
185 |
|
|
signal(SIGHUP, lambda signum, frame: sys.exit(0)) |
186 |
|
|
signal(SIGTERM, lambda signum, frame: sys.exit(0)) |
187 |
|
|
|
188 |
|
|
# OK, we're a real daemon now. |
189 |
|
|
|
190 |
|
|
BUFSIZE=8192 |
191 |
|
|
|
192 |
|
|
def stripcrnl(string): |
193 |
|
|
"Return STRING with any CR or NL stripped off the end." |
194 |
|
|
result = "" |
195 |
|
|
if string[-1:] == '\n' or string[-1:] == '\r': |
196 |
|
|
result = string[:-1] |
197 |
|
|
if result[-1:] == '\n' or result[-1:] == '\r': |
198 |
|
|
return result[:-1] |
199 |
|
|
else: |
200 |
|
|
return result |
201 |
|
|
|
202 |
|
|
# Inherit a handler class to handle every connection. |
203 |
|
|
class connectionhandler(SocketServer.BaseRequestHandler): |
204 |
|
|
def handle(self): |
205 |
|
|
if debug_flag: |
206 |
|
|
print sys.argv[0] + ": server handling connection" |
207 |
|
|
clientdata = os.fdopen(self.request.fileno(), "rb") |
208 |
|
|
# Read first line; it has the command on it. |
209 |
|
|
command = clientdata.readline() |
210 |
|
|
command = stripcrnl(command) |
211 |
|
|
if debug_flag: |
212 |
|
|
print sys.argv[0] + ": ServerCommand: " + command |
213 |
|
|
# Read headers, if any. |
214 |
|
|
message = rfc822.Message(clientdata, 0) |
215 |
|
|
if debug_flag: |
216 |
|
|
print sys.argv[0] + ": Server EOH" |
217 |
|
|
# Do stuff depending on command. |
218 |
|
|
if command == "debug": |
219 |
|
|
# Debug command, just print all the input to stdout. |
220 |
|
|
# This is only of any use if the agent is running with |
221 |
|
|
# --debug, i.e., has not closed stdout. |
222 |
|
|
if debug_flag: |
223 |
|
|
print sys.argv[0] + ": Server command: debug" |
224 |
|
|
if debug_flag: |
225 |
|
|
# print "password: " + agent.password |
226 |
|
|
while 1: |
227 |
|
|
data = clientdata.read(BUFSIZE) |
228 |
|
|
if not data: break |
229 |
|
|
if debug_flag: |
230 |
|
|
sys.stdout.write(data) |
231 |
|
|
elif command == "print": |
232 |
|
|
# Print command, pipe the input to smbclient. |
233 |
|
|
if debug_flag: |
234 |
|
|
print sys.argv[0] + ": server command: print" |
235 |
|
|
smbclientformat = "smbclient \"%s\" \"%s\" -N -P %s %s -c \"%s\"" |
236 |
|
|
# service, passwd, ip, options, command |
237 |
|
|
smbserver = "obelix" |
238 |
|
|
smbprinter = "rby_4" |
239 |
|
|
smbservice = None |
240 |
|
|
smbserverip = "-I obelix.rby.hk-r.se" |
241 |
|
|
smbclientoptions = "" |
242 |
|
|
smbclientcommand = "printmode graphics;print -" |
243 |
|
|
if message.getheader('SmbClientFormat'): |
244 |
|
|
smbclientformat = message.getheader('SmbClientFormat') |
245 |
|
|
if message.getheader('SmbServer'): |
246 |
|
|
smbserverip = "" |
247 |
|
|
smbserver = message.getheader('SmbServer') |
248 |
|
|
if message.getheader('SmbPrinter'): |
249 |
|
|
smbprinter = message.getheader('SmbPrinter') |
250 |
|
|
if message.getheader('SmbService'): |
251 |
|
|
smbserver = None |
252 |
|
|
smbprinter = None |
253 |
|
|
smbclientip = "" |
254 |
|
|
smbservice = message.getheader('SmbService') |
255 |
|
|
if message.getheader('SmbServerIP'): |
256 |
|
|
smbserverip = "-I \"" \ |
257 |
|
|
+ message.getheader('SmbServerIP')\ |
258 |
|
|
+ "\"" |
259 |
|
|
if message.getheader('SmbClientOptions'): |
260 |
|
|
smbclientoptions = message.getheader('SmbClientOptions') |
261 |
|
|
if message.getheader('SmbClientCommand'): |
262 |
|
|
smbclientcommand = message.getheader('SmbClientCommand') |
263 |
|
|
# If it wasn't explicitly set, set the service from the |
264 |
|
|
# components, default or otherwise. |
265 |
|
|
if not smbservice: |
266 |
|
|
smbservice = "//%s/%s" % (smbserver, smbprinter) |
267 |
|
|
pipecmd = smbclientformat % (smbservice,\ |
268 |
|
|
agent.password, \ |
269 |
|
|
smbserverip, \ |
270 |
|
|
smbclientoptions, \ |
271 |
|
|
smbclientcommand) |
272 |
|
|
pipe = None |
273 |
|
|
pipe = os.popen(pipecmd, "w") |
274 |
|
|
if debug_flag: |
275 |
|
|
print sys.argv[0] + ": Pipecmd: " + pipecmd |
276 |
|
|
while 1: |
277 |
|
|
data = clientdata.read(BUFSIZE) |
278 |
|
|
if not data: break |
279 |
|
|
if debug_flag: |
280 |
|
|
sys.stdout.write(data) |
281 |
|
|
else: |
282 |
|
|
pipe.write(data) |
283 |
|
|
#if not debug_flag: |
284 |
|
|
pipe.close() |
285 |
|
|
elif command == "write": |
286 |
|
|
# Write command, write input to file specified in header. |
287 |
|
|
# Used for testing. |
288 |
|
|
if debug_flag: |
289 |
|
|
print sys.argv[0] + ": server command: write" |
290 |
|
|
outfile = open(message.getheader('Filename'), "wb") |
291 |
|
|
while 1: |
292 |
|
|
data = clientdata.read(BUFSIZE) |
293 |
|
|
if not data: break |
294 |
|
|
outfile.write(data) |
295 |
|
|
outfile.close() |
296 |
|
|
elif command == "password": |
297 |
|
|
# Set password variable. |
298 |
|
|
if debug_flag: |
299 |
|
|
print sys.argv[0] + ": server command: password" |
300 |
|
|
password = clientdata.readline(80) |
301 |
|
|
agent.password = stripcrnl(password) |
302 |
|
|
del password |
303 |
|
|
elif command == "eval": |
304 |
|
|
# Eval all input. May be used for extension and |
305 |
|
|
# modification of already running agents. |
306 |
|
|
if debug_flag: |
307 |
|
|
print sys.argv[0] + ": server command: eval" |
308 |
|
|
while 1: |
309 |
|
|
line = clientdata.readline() |
310 |
|
|
if not line: break |
311 |
|
|
eval(line) |
312 |
|
|
elif command == "quit": |
313 |
|
|
clientdata.close() |
314 |
|
|
self.request.close() |
315 |
|
|
cleanup_socket() |
316 |
|
|
os._exit(0) |
317 |
|
|
else: |
318 |
|
|
if debug_flag: |
319 |
|
|
print sys.argv[0] + ": Unknown command '" + command + "'" |
320 |
|
|
clientdata.close() |
321 |
|
|
self.request.close() |
322 |
|
|
|
323 |
|
|
## Inherit a server agent class. |
324 |
|
|
# class LPRAgent(ForkingMixIn, SocketServer.UnixStreamServer): pass |
325 |
|
|
class LPRAgent(SocketServer.UnixStreamServer): |
326 |
|
|
password = "" |
327 |
|
|
|
328 |
|
|
# Instantiate an agent to listen on the socket and call the handler. |
329 |
|
|
agent = LPRAgent(socket_name, connectionhandler) |
330 |
|
|
|
331 |
|
|
if debug_flag: |
332 |
|
|
print sys.argv[0] + ": Server starting..." |
333 |
|
|
|
334 |
|
|
# Go go go! |
335 |
|
|
agent.serve_forever() |