Tuesday, September 21, 2010

Episode #113: Checking for Prints

Hal is in transit

Greetings from SANS Network Security in Las Vegas! For those of you who are in town for the conference, I'll be giving a Command Line Kung Fu talk on Wednesday at 8pm-- hope to see you there!

Right, so I needed to come up with something quick for this week because of my travel time crunch. And as I was prepping to head to Vegas, the perfect idea occurred to me as I typed the following command:

$ lp travel-calendar.txt
request id is HP-LaserJet-4050-89 (1 file(s))

For the three or four of you who still print documents from your Linux or Unix systems, let's talk about managing print jobs from the command line!

The lp command-- yes, that's short for "line printer", which tells you how old the Unix printing interface is-- is used to submit print jobs. With no additional arguments, the file(s) you're printing are just sent to the system's default printer. You can use "-P" to specify an alternate printer. You can also set the environment variable $PRINTER to choose a different default for your sessions.

You can use lpstat to peek at the status of your print jobs:

$ lpstat
HP-LaserJet-4050-89 hal 1024 Sun 19 Sep 2010 08:27:02 AM PDT

But you can also use "lpstat -a" to get the current status of all of the available printers on your system:

$ lpstat -a
HP-LaserJet-4050 accepting requests since Sun 19 Sep 2010 08:27:05 AM PDT
LJ9000N accepting requests since Thu Sep 09 11:10:54 2010 AM PDT

Finally, you can use the cancel command ("lprm" also works on most Unix systems) to stop your print jobs by job ID:

$ cancel HP-LaserJet-4050-89
$ lpstat

However, note that in the modern era of networked printers with their own local print servers, you'll have to be pretty quick with your cancel command to interrupt the print job before it gets spooled over the network to the remote printer. Once that happens, the cancel command you issue on your local machine probably won't stop the print job.

Similarly, there's also an lpmove command that allows you to switch a job from one printer to another. But again, you need to move the job before it gets spooled off your system. But lpmove can be useful when your jobs are stalled in a print queue on the local machine because the remote printer is down.

Let's see what Tim's spooling off on the Windows side of the house, shall we?

Tim's been taking his pretty little time:

I was initially going to go first on this since Hal was so busy. And I even wrote it way ahead of time, but I completely forgot to post it. I guess you could say I forgot to hit the print button.

Ok, so I can tell ahead of time that attempt at a joke failed miserably, but work with me here people, I'm trying to come up with a segway.

Soooo anyway, print jobs. In Windows we have to use our good ol' pal WMI. I'm sure no one is suprissed. Of course when it comes to cmd.exe, there only seem to be two choices, a For loop and wmic.

This week I'm going to combine the cmd.exe and PowerShell portions since they are very similar. Let's first start off by listing print jobs.

PS C:\> gwmi win32_printjob
Document JobId JobStatus Owner Priority Size Name
-------- ----- --------- ----- -------- ---- ----
Command Line Kung Fu 6 tim 1 331444 Canon MX350
Command Line Kung Fu 7 tim 1 331444 Canon MX350

C:\> wmic printjob get "Document,JobId,Name,Owner,Priority,Size"
Document JobId Name Owner Priority Size
Command Line Kung Fu 5 Canon MX350, 5 tim 1 228396
Command Line Kung Fu 6 Canon MX350, 5 tim 1 228396
We can select a specific job:

PS C:\> gwmi win32_printjob | ? { $_.JobId -eq 5 }
C:\> wmic printjob where JobId=5 get "Document,JobId,Name,Owner,Priority,Size"
And we can kill that job:
PS C:\> gwmi win32_printjob | ? { $_.JobId -eq 5 } | % { $_.Delete }
C:\> wmic printjob where JobId=5 delete
Or kill all jobs:

PS C:\> gwmi win32_printjob | % { $_.Delete }
C:\> wmic printjob delete
And that wraps up the print job portion, but I wanted to address one weird thing I discovered in with WMI. For some reason, WMI doesn't tell PowerShell that it has a delete method for print jobs.

PS C:\> gwmi win32_printjob | gm -MemberType Method

TypeName: System.Management.ManagementObject#root\cimv2\Win32_PrintJob

Name MemberType Definition
---- ---------- ----------
Pause Method System.Management.ManagementBaseObject Pause()
Resume Method System.Management.ManagementBaseObject Resume()
We use Get-Member (alias gm) to look at the members of an object. We use the -MemberType parameter to specifically look for methods. But it doesn't show the delete method, even though it does work as we can see above. Wierd right? I know you are suprised, weirdness in WMI, right? Of cource after working with WMI so much I'm pretty sure that the W in WMI stands for weirdness.