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.bth.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.bth.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 to 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 to 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 = "slade" |
238 |
smbprinter = "rby_expen" |
239 |
smbservice = None |
240 |
smbserverip = "-I slade.student.bth.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() |